Over the weekend, a friend added my wechat and asked me a question: Brother, what is the difference between a Filter and an Interceptor? Hearing the title, my first feeling is: simple!

After all, these two tools are used in the development of the frequency is quite high, the application is relatively simple, but when I am ready to reply to him, actually do not know where to start, hesitated for a long time, the scene fried chicken embarrassing, work so long a basic question to answer so, lost adults.

Usually think simple knowledge points, but usually do not pay too much attention to the details, once asked by others, but can not say why.

In the final analysis, or the knowledge of these is not enough, has been staying in the stage of use, so that now a look will be useless! This is a typical foundation is not solid performance, ah ·~, in fact, I also is a fat!

Know shame and then brave, the following combined with practice, more intuitive to feel what the difference between the two?

Prepare the environment

We configured both interceptors and filters in the project.

1. Filter

Filter configuration is relatively simple. You can directly implement the Filter interface or use the @webFilter annotation to intercept specific URLS. You can see that three methods are defined in the Filter interface.

  • Init () : This method is called when the container starts the initialization Filter, and it is called only once during the Filter’s lifetime. Note: This method must execute successfully, otherwise the filter will not work.
  • DoFilter () : This method is called for each request in the container, and the FilterChain is used to call the next Filter Filter.
  • Destroy () : this method is called when the container destroys the Filter instance, usually destroying or closing the resource in the method, and only once during the lifetime of the Filter
@Component
public class MyFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("Filter front");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter processing");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println(Rear "Filter"); }}Copy the code

2. Interceptor

Interceptors are chained calls. An application can have multiple interceptors at the same time. A request can also trigger multiple interceptors, and each Interceptor call is executed in the order in which it is declared.

Firstly, write a simple interceptor handling class. The interceptor is implemented by the HandlerInterceptor interface. See that the HandlerInterceptor interface also defines three methods.

  • PreHandle () : This method is called before the request is processed. Note: If the return value of this method is false, the current request is considered complete and not only the interceptor itself will be invalidated, but other interceptors will no longer execute.
  • PostHandle () : Executes only if the preHandle() method returns true. Is called after a method call in Controller but before the DispatcherServlet returns to the render view. Interestingly, the postHandle() method is called in the opposite order to the preHandle() method. The declared interceptor preHandle() method executes first, while the postHandle() method executes later.
  • AfterCompletion () : Will be executed only if the preHandle() method returns true. At the end of the entire request, the DispatcherServlet renders the corresponding view and executes.
@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("Interceptor front");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("Interceptor is in process");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("Rear Interceptor"); }}Copy the code

Register your custom interceptor handling classes and set the urls that need to be blocked or excluded through properties like addPathPatterns, excludePathPatterns, and so on.

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/ * *");
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/ * *"); }}Copy the code

We are not the same

Both filters and interceptors embody the programming ideas of AOP and can implement functions such as logging, login authentication, and so on, but there are a number of differences between them, which are described in the following sections.

1. Different implementation principles

Filters and interceptors are implemented in a very different way. Filters are based on function callbacks, while interceptors are implemented based on Java’s reflection mechanism (dynamic proxy).

Here focus on the filter!

Each of our custom filters implements a doFilter() method that takes a FilterChain parameter, which is actually a callback interface. ApplicationFilterChain is its implementation class, which also has an internal doFilter() method called the callback method.

public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}Copy the code

ApplicationFilterChain can get our custom xxxFilter class, call each custom xxxFilter filter in its internal callback method doFilter(), and execute the doFilter() method.

public final class ApplicationFilterChain implements FilterChain {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) { ... / / omit internalDoFilter (request, response); } private void internalDoFilter(ServletRequest request, ServletResponse response){if(pos < n) {// Get the pos filter ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = filterConfig.getFilter(); . filter.doFilter(request, response, this); }}}Copy the code

Each xxxFilter will first execute its own doFilter() filtering logic, and finally filterchain.dofilter (servletRequest, servletResponse) will be executed before the end of the execution, This loop implements the function callback by calling back to the ApplicationFilterChain’s doFilter() method.

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }Copy the code

2, the scope of use is different

We see that the Filter implements the Javax.servlet.filter interface, which is defined in the Servlet specification, meaning that the use of Filter is dependent on containers such as Tomcat, making it only available in Web applications.

The Interceptor is a Spring component that is managed by the Spring container and does not depend on a container such as Tomcat. It can be used independently. Not only can be applied in web programs, but also can be used in Application, Swing and other programs.

3, the trigger time is different

Filters and interceptors also fire at different times, as shown in the chart below.

The Filter is preprocessed after the request enters the container, but before it enters the servlet, and the request ends after the servlet completes processing.

The Interceptor preprocesses the request after it enters the servlet and before it enters the Controller, where the request ends after the corresponding view is rendered.

4. The range of requests intercepted is different

We have configured both filters and interceptors, and we will test this by creating a new Controller to receive requests.

@Controller
@RequestMapping()
public class Test {

    @RequestMapping("/test1")
    @ResponseBody
    public String test1(String a) {
        System.out.println("I am the controller");
        returnnull; }}Copy the code

The init() method of the filter, discovered during project startup, is initialized with container startup.

When the browser sends a request, F12 sees that there are two requests, one for our custom Controller and the other for access to the static icon resource.

The console log is as follows:

Execution order: Filter processing -> Interceptor front -> I am controller -> Interceptor processing -> Interceptor after processing

Interceptor precedes Interceptor in Filter processing; Interceptor precedes Filter processing in Interceptor processingCopy the code

The Filter is executed twice, while the Interceptor is executed only once. This is because filters work on almost all requests that come into the container, whereas interceptors work only on requests in the Controller or on resource requests in the static directory.

5. Injection beans are different

In a real business scenario, applying a filter or interceptor will inevitably introduce some service services to process the business logic.

Now let’s inject Service into both filters and interceptors. What’s the difference?

@Component
public class TestServiceImpl implements TestService {

    @Override
    public void a() {
        System.out.println("I am Method A"); }}Copy the code

Insert service into the filter and make A request to test it. The log prints “This is method A”.

@Autowired
    private TestService testService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter processing");
        testService.a();
        filterChain.doFilter(servletRequest, servletResponse);
    }Copy the code


In the Filter processing, I am preceded by the A Interceptor method and I am preceded by the Controller Interceptor methodCopy the code

Insert the service into the interceptor, send a request to the interceptor, and check whether the service is Null.

This is due to a load order issue, where the interceptors are loaded before The SpringContext, and the beans are managed by Spring.

Interceptor: I’m going into the bridal chamber today;


Spring: Dude, don’t mess around. Your daughter-in-law hasn’t been born yet!

The solution is as simple as injecting Interceptor manually before registering it. Note: The instance of getMyInterceptor() is registered in Registry.addInterceptor ().

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
        System.out.println("MyInterceptor is injected");
        return new MyInterceptor();
    }
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/ * *"); }}Copy the code

6, the control execution sequence is different

In the actual development process, there will be multiple filters or interceptors at the same time, but sometimes we want one filter or interceptor to be executed first, which involves the order in which they are executed.

Filters use @Order annotation to control the Order of execution. The level of the filter is controlled by @Order. The smaller the value, the higher the level.

@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyFilter2 implements Filter {Copy the code

The default Order in which interceptors are executed is the Order in which they are registered. This can also be controlled manually with Order, where the smaller the value, the more the interceptors are executed first.

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/ * *").order(2);
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/ * *").order(1);
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/ * *").order(3);
    }Copy the code

When you look at the output, you see that the declared interceptor preHandle() method executes first, while the postHandle() method executes later.

PostHandle () is called in the opposite order to preHandle()! If your actual development is strictly about execution order, you need to pay special attention to this.

I'm the Controller Interceptor. I'm the controller Interceptor. I'm the Interceptor1 Interceptor after Interceptor2 after Interceptor1 after processingCopy the code

So why is that? The only way to get the answer is to look at the source code, and know that all requests in the Controller are routed through the core DispatcherServlet component, executing its doDispatch() method, This is where the interceptor postHandle() and preHandle() methods are called.

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { try { ........... HandlerAdapter ha = getHandlerAdapter(mappedhandler.gethandler ()); // Process last-modified header,if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return; }} // Note: Perform the PreHandle() method in Interceptorif(! mappedHandler.applyPreHandle(processedRequest, response)) {return; } mv = ha.handle(processedRequest, response, mappedhandler.gethandler ());if (asyncManager.isConcurrentHandlingStarted()) {
                    return; } applyDefaultViewName(processedRequest, mv); / / note: the implementation method of the Interceptor PostHandle mappedHandler. When an exception is unable to perform 】 【 applyPostHandle (processedRequest, response, mv); }}... }Copy the code

Take a look at how the two methods applyPreHandle() and applyPostHandle() are called, and you can see why postHandle() and preHandle() are executed in reverse order.

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(! ObjectUtils.isEmpty(interceptors)) {for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if(! interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null);return false; }}}return true;
    }Copy the code


void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if(! ObjectUtils.isEmpty(interceptors)) {for(int i = interceptors.length - 1; i >= 0; --i) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); }}}Copy the code

The HandlerInterceptor[] interceptor array is called in the same order as the HandlerInterceptor[]. Causes the postHandle() and preHandle() methods to execute in reverse order.

conclusion

I believe that most people are able to skillfully use filter and interceptors, but the difference still need to know more, improper use or development, and from time to time would be a strange question, the above content is simpler, novice learning veteran review, there are omissions place still hope everybody actively, if there is any wrong place, still hope glad.

SegmentFault: SegmentFault: SegmentFault: SegmentFault: SegmentFault


Click follow to learn about the fresh technologies of Huawei Cloud