3

In the era of microservices, Java relied on SpringBoot to increase its popularity again. I originally thought that php, python and the like would continue to erode the territory of Java, and familiarity with microservices has restored Java's former status. SpringBoot relies on the Spring ecosystem to be full of fans, and the popularity is even better than that of the year.

The essence of SpringBoot is to realize automatic assembly, which solves the problem of configuration area developed by Spring. But its foundation is still Spring, and the foundation for web development is still SpringMVC. Therefore, it is necessary to have a deep understanding of Spring, SpringMVC.

For Spring, interceptors and filters are two very core and important concepts. Therefore, this article conducts an in-depth analysis of these two concepts, allowing us to thoroughly understand interceptors and filters.

There are a lot of articles about what interceptors and filters are, and we will not elaborate on them here. This article is divided into five parts:

  1. The difference between interceptor and filter
  2. Interceptor use
  3. Filter use
  4. Application scenarios of interceptors and filters
  5. The execution process and flow of interceptors and filters

1. The difference between interceptor and filter

FilterInterceptorto sum up
The Filter interface is defined in the javax.servlet packageThe HandlerInterceptor interface is defined in the org.springframework.web.servlet package
Filter is defined in web.xmlHandlerInterceptor is configured in the application context.
Filter only works before and after Servlet. Filters usually treat requests and responses as black boxes, and Filters usually do not consider the implementation of Servlet.Interceptors can go deep before and after methods, before and after exceptions are thrown, so the use of interceptors has greater flexibility. To allow users to hook into the life cycle of the request and obtain information during the request process, the Interceptor is usually more coupled with the request.In the Spring architecture program, the use of interceptors is limited. Almost everything Filter can do, Interceptor can be easily achieved.
Filter is specified by the Servlet specification.The interceptor can be used in Web programs, as well as in Application and Swing programs.Different scope of use
Filter is defined in the Servlet specification and is supported by the Servlet container.The interceptor is in the Spring container and is supported by the Spring framework.Different specifications
Filter cannot use Spring container resources.The interceptor is a Spring component, managed by Spring, and configured in the Spring file. Therefore, any resources and objects in Spring, such as Service objects, data sources, transaction management, etc., can be used by injecting them into the interceptor through IoC.It is easier to use interceptors in Spring
Filter is called by Servlet (just like Tomcat).Interceptor is called by Spring.Therefore, Filter is always better than Interceptor execution.

Let's quote a picture of others to illustrate the relationship between interceptors, filters, Servlets, and Spring Controllers under the tomcat container.

The execution flow and relationship of components such as filters and interceptors during request processing and response:

  1. The request first arrives at the Servlet container and forwards the request to the web container.
  2. The web container invokes the Servlet filter, and then continues to forward the request to Spring's DispatcherServlet, and the request is forwarded to the Spring context.
  3. DispatcherServlet can then call Spring's interceptor to intercept the request, and finally hand it over to the resource controller.
  4. Finally, the resource controller combines the business model to process business, and then processes the corresponding outwards along the 4 to 1 layer, and finally returns to the client.

2. Use of interceptors

The execution order of Interceptor is roughly as follows:

  • Request arrives at DispatcherServlet
  • DispatcherServlet sent to Interceptor, execute preHandle
  • Request arrives at Controller
  • After the request is over, postHandle is executed

In Spring, the interception of requests is mainly implemented through the HandlerInterceptor interface. To implement the HandlerInterceptor interface, the following three methods need to be implemented:

  • preHandle(): Before the handle is executed, it returns a boolean value, true means to continue execution, false means to stop execution and return.
  • postHandle(): After the handle is executed, the returned result can be modified before returning.
  • afterCompletion(): Called after the request is completed and the view is generated.

2.1 Use interceptors to perform request time-consuming statistics

Request time-consuming statistics can be recorded by recording a time before the request is processed, and then recording a time after the request is processed, and printing the difference between the two times is the request time.

2.1.1 Define an interceptor

public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter {
    // private static final Logger log = LoggerFactory.getLogger(ExecuteTimeInterceptor.class);

    /**
     * 在handler执行之前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return super.preHandle(request, response, handler);
    }

    /**
     * 在handler执行之后执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;

        System.out.println("[" + handler + "] executeTime : " + executeTime + "ms");
        super.postHandle(request, response, handler, modelAndView);
    }
}

2.1.2 Configure Interceptor

As mentioned earlier, we know that the interceptor is something in the Spring context, so we need to configure it in the Spring context. The configuration method using springmvc-servlet.xml is as follows:

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.xxx.interceptor.ExecuteTimeInterceptor" />
  </mvc:interceptor>
</mvc:interceptors>

2.1.3 Run test

Run it, and then access an interface, you will see the console output as follows:

2.2 Use interceptors to implement application security checks

Usually our applications are divided into front-end applications and management back-end applications, and their security check strategies are different. We can implement different security policy checks through interceptors. We will not list the specific interceptor logic implementation, just a brief description:

  1. For all front-end application requests, general security checks are performed. Let's let the FrontSecurityInterceptor interceptor to complete the specific business for the time being.
  2. For all back-end application requests, stricter security checks are performed, because background data is very important. Let's use BackendSecurityInterceptor interceptor to complete this difficult task.
  3. In addition, our front-end applications and management-side applications are deployed under the same domain name www.example.com, the front-end applications can directly access this link, and the back-end management can be accessed by adding the prefix /admin.

Then we can configure the interceptor as follows:

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/admin/**" />
    <bean class="com.yang.interceptor.FrontSecurityInterceptor" />
  </mvc:interceptor>
  <mvc:interceptor>
    <mvc:mapping path="/admin/**"/>
    <bean class="com.yang.interceptor.BackendSecurityInterceptor" />
  </mvc:interceptor>
</mvc:interceptors>

2.3 Summary of Spring Interceptors

We can use the mvc:interceptors tag to declare a series of interceptors, and then they can form an interceptor chain. The execution order of the interceptors is executed in the order of declaration. The preHandle method in the interceptor declared first will be executed first , But its postHandle method and afterCompletion method will be executed later.

There are two main ways to declare interceptors under the mvc:interceptors tag:

  1. Directly define a bean object of the Interceptor implementation class. The Interceptor declared in this way will intercept all requests.
  2. Use the mvc:interceptor tag to declare. The Interceptor declared in this way can define the request path that needs to be intercepted through the mvc:mapping subtag.

3. Use of Filter

The Filter interface of Servlet needs to implement the following methods:

  • void init(FilterConfig paramFilterConfig) -Called when the container initializes the Filter. This method will only be called once in the life cycle of the Filter. Generally, some resources are initialized in this method. FilterConfig is the initialization parameter provided by the container to the Filter. ServletException can be thrown in this method. The init method must be executed successfully, otherwise the Filter may not work. In the following two situations, the Filter in the web container may be invalid:

    • Throw ServletException
    • The execution time defined by the web container was exceeded.
  • doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) -Web container will call this method for every request. This method passes the request and response of the container as parameters, and the FilterChain is used to call the next Filter.
  • void destroy() -This method is called when the container destroys the Filter instance, and the resource can be destroyed in the method. This method will only be called once in the life cycle of the Filter.

Some uses of Filter and Interceptor

The following are some uses of the filters listed in the Filter interface source code

  • Authentication Filters
  • Logging and Auditing Filters
  • Image conversion Filters
  • Data compression Filters
  • Encryption Filters
  • Tokenizing Filters
  • Filters that trigger resource access events
  • XSL/T filters
  • Mime-type chain Filter

4.1 Application Scenarios of Request Filter

  • Perform security checks
  • Format request headers or bodies (reformat request headers or bodies)
  • Audit or log requests
  • Authorize or restrict user access based on request content (Authentication-Blocking requests based on user identity)
  • Restrict user access based on request frequency

4.2 Application Scenarios of Response Filters

  • Compress the response content, such as making the downloaded content smaller (Compress the response stream)
  • Append or modify the response (append or alter the response stream)
  • Create or modify the response as a whole (create a different response altogether)
  • Modify the response content according to different places (Localization-Targeting the request and response to a particular locale)

5. Verify the filter and interceptor execution process

5.1 Define two filters and interceptors

Let's verify the execution process and sequence of filters and interceptors:

  1. Define two filters and configure them in web.xml: FirstFilter and SecondFilter, which filter all requests.
  2. Define two interceptors and configure them in the spring context. Here, configure them in springmvc-servlet.xml: FirstInterceptor and SecondInterceptor.

The two Filter implementations are basically similar:

// Filter
public class FirstFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(this.getClass().getName() + ": init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println(this.getClass().getName() + ": doFilter");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        System.out.println(this.getClass().getName() + ": destroy");
    }
}

The implementation of the two Interceptors is also basically similar:

public class FirstInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(this.getClass().getName() + ": preHandle");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(this.getClass().getName() + ": postHandle");
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(this.getClass().getName() + ": afterCompletion");
        super.afterCompletion(request, response, handler, ex);
    }
}

5.2 Configure filters and interceptors

Configure the filter in web.xml:

<!-- 定义两个filter -->
<filter>
  <filter-name>firstFilter</filter-name>
  <filter-class>com.yang.filter.FirstFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>firstFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
  <filter-name>secondFilter</filter-name>
  <filter-class>com.yang.filter.SecondFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>secondFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Configure the interceptor in springmvc-servlet.xml:

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.yang.interceptor.ExecuteTimeInterceptor" />
  </mvc:interceptor>

  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.yang.interceptor.FirstInterceptor" />
  </mvc:interceptor>

  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.yang.interceptor.SecondInterceptor" />
  </mvc:interceptor>
</mvc:interceptors>

5.3 Verification result

Then run Tomcat, you can see that the init() methods of FirstFilter and SecondFilter are executed at startup.

After we start tomcat, we automatically open a jsp page: we see that the doFilter of the two filters is also executed:

Then we visit an API interface: GET /demo/hello, we see the console output:

5.4 Summary

  1. The filter is called by the container, and the init() method of the filter is called when the container starts.
  2. The filter is for all requests, including ordinary jsp pages, such as the homepage.
  3. The execution order of multiple filters is executed in the order configured in web.xml.
  4. The interceptor is executed in the Spring context, when the request is forwarded to Spring after the container is started.
  5. The execution order of multiple interceptors preHandle is executed in the order configured in the container.
  6. The execution order of multiple interceptors afterHandle is executed in the reverse order configured in the container.
  7. The afterCompletion execution order of multiple interceptors is executed in the reverse order configured in the container, and executed after all afterHandle executions are completed.

The code word is not easy, if it is useful to everyone, please pay attention to "Old Beggar Says Code".

The old beggar said the code: Focus on Java R&D, SpringBoot, Spring Cloud, and continue to withdraw a series of related articles later.

In addition, I recommend a free and easy-to-use local markdown editor: Typora

Code address: springmvc-springmvc-handleflow
Reference article:


老将廉颇
878 声望297 粉丝