Spring MVC (abstractcontroller, interceptor, annotation)

Keywords: Java Spring xml JSP Session

1.Controller interface and its implementation class
Controller is a controller / processor interface, with only one method handleRequest, which is used to process the function of the request (function processing method). After processing the request, it returns the ModelAndView object (Model data part and View view View part).

If you want to write back data to the client directly using response in the processor / controller, you can tell the dispatcher servlet by returning null that we have written out the response and do not need it to parse the view

Spring provides some implementation classes of the Controller interface by default for our convenience. Select the Controller interface in Eclipse and right-click open type Hierarchy to view the implementation classes of the interface. Each implementation class has its own special functions. Here is a brief introduction of the implementation class AbstractController.
Looking at the code in the AbstractController class, we can see that when we write a Controller, we can inherit AbstractController and then implement the handleRequestInternal method.

The serial access function of [optional] session is provided, for example:
The same session, thread synchronization

public class HelloWorldController extends AbstractController{
  @Override
  protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)throws Exception {

  String name = request.getParameter("name");

  //ModelAndView Object contains the logical view to return,And data model
  ModelAndView mv = new ModelAndView();
  //Set view name,Can be a string or a view object
  mv.setViewName("hello");
  //Set up data model
  mv.addObject("name", name);

  return mv;
  }
}

<bean name="/hello" class="com.briup.web.controller.HelloWorldController">
  <property name="synchronizeOnSession" value="true"></property>
</bean>

 

Write the response directly through response, for example:

public class HelloWorldController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {

response.getWriter().write("Hello World!!");    
//If you want to go directly to the processor/The controller write response can be returned by null tell DispatcherServlet I have written the response,It is not needed for view resolution
return null;
}
}

 

Force request method type, for example:
Only post and get methods are supported

<bean name="/hello" class="com.briup.web.controller.HelloWorldController">
<property name="supportedMethods" value="POST,GET"></property>
</bean>

 

If there is no session in the current request, HttpSessionRequiredException will be thrown, for example:
When entering the controller, there must be a session, otherwise an HttpSessionRequiredException is thrown.

<bean name="/hello" class="com.briup.web.controller.HelloWorldController">
<property name="requireSession" value="true"/>
</bean>

 

2. Interceptor in springmvc

Spring MVC's processor interceptor is similar to the Filter filter in Servlet development, which is used to preprocess and postprocess the processor. The role of interceptors is limited to the processor

1) common application scenarios
1. Log record
2. Authority check
3. Performance monitoring
4. General behaviors such as reading user cookie s

2) interceptor interface

public interface HandlerInterceptor {
  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;

  void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;

  void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}

 

preHandle method
Preprocessing callback method to realize preprocessing of the processor. The third parameter is the processor (the controller to be accessed in this request)
Return value: true means to continue the process (if the next interceptor or processor is called)
false means that the process is interrupted (such as login check failure), and other interceptors or processors will not be called. At this time, we need to generate response through response

postHandle method
The post-processing callback method implements the post-processing of the processor (but before rendering the view). At this time, we can process the model data or the view through modelAndView, which may also be null.

afterCompletion method
Callback method after the whole request is processed, i.e. when the view is rendered

3) interceptor adapter
Sometimes we only need to implement one of the three callback methods. If we implement the HandlerInterceptor interface, the three methods must be implemented. No matter whether you need it or not, spring provides a HandlerInterceptor adapter (adapter mode), which allows us to implement only the callback methods we need.
In the HandlerInterceptorAdapter, three methods in the HandlerInterceptor interface are null implemented. The return value of the preHandle method is true by default

4) test an interceptor
Interceptor Code:

public class MyInterceptor1 extends HandlerInterceptorAdapter{
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    System.out.println("MyInterceptor1 preHandle");
    return true;
  }
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
    System.out.println("MyInterceptor1 postHandle");
  }
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
    System.out.println("MyInterceptor1 afterCompletion");
  }
}

 

Configuration file: (note the configuration order of this configuration in the file, which should be written on the top of the configuration file)

<bean name="handlerInterceptor1" class="Package name.MyInterceptor1"/>

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
  <property name="interceptors">
    <list>
      <ref bean="handlerInterceptor1"/>
    </list>
  </property>
</bean>

 

Access the Controller of a test to view the results:
MyInterceptor1 preHandle
TestController execution
MyInterceptor1 postHandle
MyInterceptor1 afterCompletion

5) test two interceptors
The code of the two interceptors is similar to the above, but the content of each output is different
Profile:

<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor2"/>

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>

 

Access the Controller of a test to view the results:
MyInterceptor1 preHandle
MyInterceptor2 preHandle
TestController execution
MyInterceptor2 postHandle
MyInterceptor1 postHandle
MyInterceptor2 afterCompletion
MyInterceptor1 afterCompletion

Note: the order in which interceptors are referenced in < list > tags affects the order in which results are output

6) configuration of interceptor mvc tag
Note: only one interceptor can be configured for each < MVC: interceptor >

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="handlerInterceptor1"/>
  </mvc:interceptor>
</mvc:interceptors>

 

For example, 1: notice the difference between / * and / *

<bean name="myInter1" class="com.briup.web.interceptor.MyInterceptor1" />

<bean class="com.briup.web.interceptor.MyInterceptor2" />

<bean class="com.briup.web.interceptor.MyInterceptor3" />

<!-- mvc Interceptor configuration mode provided -->
<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="myInter1"/>
  </mvc:interceptor>

  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/test"/>
    <ref bean="myInter2"/>
  </mvc:interceptor>

  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="timeInter"/>
  </mvc:interceptor>
</mvc:interceptors>

7) interceptor is a single example
Therefore, no matter how many user requests and how many times, there is only one interceptor implementation, that is, the thread is not safe.
Therefore, if necessary, you can use ThreadLocal in the interceptor. It is bound to A thread. One thread has one ThreadLocal. The ThreadLocal of thread A can only see the ThreadLocal of thread A, but not the ThreadLocal of thread B.
for instance:
Record the time taken to execute the Controller

public class TimeInterceptor extends HandlerInterceptorAdapter{
  //The interceptor is a single example,Not thread safe,So here we use ThreadLocal
  private ThreadLocal<Long> local = new ThreadLocal<>();

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    long start = System.currentTimeMillis();
    local.set(start);
    return true;
  }
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
    long end = System.currentTimeMillis();
    System.out.println("Total time consuming:"+(end-local.get()));
  }
}

8) login check

public class LoginInterceptor extends HandlerInterceptorAdapter{
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
  //Request to login page for release
    if(request.getServletPath().startsWith("/login")) {
      return true;
    }

  //If the user has logged in and released
    if(request.getSession().getAttribute("username") != null) {
      return true;
    }

  //Other cases without login will be redirected to the login page
  response.sendRedirect(request.getContextPath() + "/login");

  return false;
  }
}

Note: it is recommended to use Filter to implement the functions in servlet specification, because handlerinterceptor can only be used in spring webmvc environment, so Filter is the most common and should be used first.

3. Annotation based spring MVC

1) configuration to support annotation
Using annotation based configuration can omit many operations and is more convenient. For all the xml configurations we have seen before, if you replace them with annotation based configurations, you only need to configure them in the spring xml file as follows:
<mvc:annotation-driven/>
In Spring
Processor classes can use @ Controller annotation
The business logic layer can use @ Service annotation
Data persistence layer can use @ Repository annotation

If you use the @ Controller annotation on the processor, you also need to specify in the configuration file which class below the package uses the annotation:
<context:component-scan base-package="com.briup.web.controller"></context:component-scan>

    <! -- enable container to recognize mvc comments -- >
    <mvc:annotation-driven></mvc:annotation-driven>
    <! -- scan all comments under the specified package and its subpackages -- >
    <context:component-scan base-package="com.briup.annotation"/>

 

2) annotation based Controller
After using annotations, there is no need to implement a specific interface. Any javaBean object can be used as a processor object, and any method in the object can be used as a processor method.
Only need
Add @ Controller annotation to the class
Method with @ RequestMapping annotation
that will do

For example:
web.xml:

<servlet>
  <servlet-name>SpringMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-web-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

src in spring-web-mvc.xml:

<mvc:annotation-driven/>
<context:component-scan base-package="com.briup.web.controller"></context:component-scan>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> 
  <property name="prefix" value="/WEB-INF/jsp/"/> 
  <property name="suffix" value=".jsp"/> 
</bean>

In the customized Controller:

@Controller
public class HomeController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

 

As shown in the above code, @ Controller is used to indicate that the HomeController class is a processor class, and @ RequestMapping("/home") is used to indicate that when the url request name is / home, the home method is called for processing, and the ModelAndView object is returned after processing. Because the prefix and suffix of the view parser are configured in spring-web-mvc.xml, the view home.jsp is returned at last

3) return value of annotation based Controller

1. Return to ModelAndView, as before

2. Return String, indicating the name of the jump logical view. The model can be passed through the parameter

@Controller
public class HomeController {
  @RequestMapping("/home")
  public String home(Model model){
    model.addAttribute("msg", "hello world");
    return "index";
  }
}

3. Declare the return type as void
You can obtain request and response through parameters, and use server internal jump and redirection respectively to determine the location to jump.

@Controller
public class HomeController {
@RequestMapping("/home")
  public void home(HttpServletRequest request,HttpServletResponse response){
    String username = request.getParameter("username");
    response.setContentType("text/html;charset=utf-8");
    response.getWriter().write("hello world! "+username);
    //Or use servlet The way to jump/redirect
  }
}

You can write a class to test all methods:

@Controller
public class HelloController {
    
    //@RequestMapping("/test1")
    //@RequestMapping(value = "/test1")
    @RequestMapping(value= {"/test1","/annotest1"})
    public ModelAndView test1(HttpServletRequest req,
            HttpServletResponse reqs) throws Exception{
        
        ModelAndView mv = new ModelAndView("test");
        mv.addObject("name", "Li Si");
        return mv;
    }
    
    //If the return value of the method is of type String,Indicates the returned logical view name
    @RequestMapping("/test2")
    public String test2() throws Exception{
        return "hello";
    }
    
    @RequestMapping("/test3")
    public String test3(Model model) throws Exception{
        model.addAttribute("name", "Wang Wu");
        return "hello";
    }
    
    @RequestMapping("/test4")
    public void test4(HttpServletResponse response) throws Exception{
        response.getWriter().write("hello world!");
        response.getWriter().flush();
    }
    
    /*
     * If the method does not return a value, and does not write back the data to the client by echoing the application flow.
     * Then, spring MVC will automatically use the parameters in @ RequestMapping as the logical view name
     */
    @RequestMapping("/test5")
    public void test5() throws Exception{
        System.out.println("--------------");
    }
    
    @RequestMapping("/test6")
    public void test6(HttpServletRequest request,
            HttpServletResponse response) throws Exception{
        String path = "/WEB-INF/jsp/hello.jsp";
        request.setAttribute("name", "Zhao Si");
        request.getRequestDispatcher(path).forward(request, response);
    }
    
    @RequestMapping("/test7")
    public void test7(HttpServletRequest request,
            HttpServletResponse response) throws Exception{
        String path = "/WEB-INF/jsp/hello.jsp";
        response.sendRedirect(request.getContextPath()+"/test6");
    }
}

 

4) spring 2.5 introduces annotation to support controller / Handler
@Controller
Used to identify the controller / processor class;
@RequestMapping
Mapping rules from request to processor function method;
@RequestParam
Binding of request parameters to method parameters of processor function processing methods;
@ModelAttribute
Binding of request parameters to command objects;
@SessionAttributes
It is used to declare the properties stored at the session level, which are placed on the processor class. Usually, the names of model properties (such as @ ModelAttribute) are listed, and these properties will be transparently saved to the session
@InitBinder
User defined data binding registration support, which is used to convert request parameters to corresponding types of command object properties;


5). Spring 3 introduces more annotations, including support for RESTful architecture style
@CookieValue
Binding of cookie data to method parameters of processor function processing method;
@RequestHeader
Binding of request header data to method parameters of processor function processing method;
@RequestBody
Binding of the body of the request
@ResponseBody
Return value of processor function processing method as response body
@ResponseStatus
Define processor function handling method / status code returned by exception processor and reason;
@ExceptionHandler
Annotated declaration exception handler;
@PathVariable
The binding of the template variable part of the request URI to the method parameter of the processor function processing method, so as to support the RESTful architecture style URI;

6). mvc namespace introduced in spring 3
The mvc namespace was introduced in spring 3 to support mvc configuration
You need to declare the value of this namespace and its corresponding schemaLocation in < beans >
<mvc:annotation-driven>
Automatically register mapper and adapter based on annotation style: (that is, this mvc tag is annotation based)
In spring 2.5 are DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter

In spring3 are RequestMappingHandlerMapping and RequestMappingHandlerAdapter
At the same time, it supports all kinds of data converters

Configure a custom processor interceptor, for example:

<mvc:interceptors>
    <mvc:interceptor>
      <mvc:mapping path="/**"/>
        <ref bean="handlerInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>

After receiving the corresponding request, select the corresponding view directly, for example:
<mvc:view-controller path="/hello" view-name="test"></mvc:view-controller>

The correspondence between the logical static resource path and the physical static resource path. For example, the problem of static resource interception is solved

<mvc:resources mapping="/images/**" location="/images/"/> 
<mvc:resources mapping="/js/**" location="/js/"/> 
<mvc:resources mapping="/css/**" location="/css/"/> 

<mvc:default-servlet-handler>

When DispatcherServlet uses < URL pattern > / < URL pattern > mapping in web.xml, the static resources will also be mapped. If the mvc tag is configured, when accessing the static resources, it will be transferred to the default Servlet to respond to the static files. Otherwise, it will report 404 no static resource error.

7).@Controller and @ RequestMapping annotation

1 claim processor

@Controller
public class HelloWorldController {

}

 

2. Function processing method in mapping processor

@Controller
public class HelloWorldController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

Indicates that the url path mapped by this method is / home

3@RequestMapping can also be written on the processor class

// /test/home
@RequestMapping("/test")
@Controller
public class HomeController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

Indicates that the url path mapped by this method is / test/home

8) request mapping
Suppose the browser sends a request as follows:
-------------------------------
POST /login HTTP1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,en;q=0.8,zh;q=0.5,en-US;q=0.3
Connection: keep-alive
Cookie: JSESSIONID=DBC6367DEB1C024A836F3EA35FCFD5A2
Host: 127.0.0.1:8989
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0

username=tom&password=123
--------------------------------


The request format of http protocol is as follows:
---------------------------------
Request method URL protocol version number
Request header information
Request header information
Request header information
..
Carriage return
Request Content
---------------------------------


From the format, we can see that the four parts of [request method, URL, request header information, request body] are generally variable, so we can map the information in the request in the [function processing method] of the processor, so the mapping of the request can be divided into the following types:
URL path mapping
The function processing method of using URL mapping to processor;
Request method mapping qualification
For example, the limited function processing method only processes GET requests;
Request parameter mapping qualification
For example, it is limited to process only requests with username parameter;
Request header mapping qualification
For example, restrict the processing of only requests with "Accept=application/json".

Posted by rick007 on Sun, 03 Nov 2019 03:49:11 -0800