It's amazing that the implementation of Servlet Filter and Spring MVC Interceptor is so simple

Posted by samyl on Mon, 20 Apr 2020 18:58:08 +0200

Preface

Creation mode: Singleton mode, factory mode, builder mode, prototype mode Structural type: bridge mode, agent mode, decorator mode, adapter mode, facade mode, combination mode, enjoyment mode Behavioral: observer mode, template mode, strategy mode, responsibility chain mode, state mode, iterator mode, visitor mode

introduce

In our work, we often have to deal with Servlet Filter and Spring MVC Interceptor. Although I've configured and written 6 things, I still get google when there is a problem. So I look at the source code and use Demo to analyze how the two work.

Servlet Filter

Use of Filter

Maybe a lot of kids haven't used Filter, so I'll give you a simple demonstration

1. Configure 2 filters in web.xml

<filter-mapping>
    <filter-name>logFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>imageFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2. The implementation is as follows, omitting init method and destroy method

@WebFilter(filterName = "logFilter")
public class LogFilter implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("LogFilter execute");
        chain.doFilter(request, response);
    }
}
@WebFilter(filterName = "imageFilter")
public class ImageFilter implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("ImageFilter execute");
        chain.doFilter(request, response);
    }
}

3. Then you access any servlet method, and the doFilter methods of LogFilter and ImageFilter will execute

If you do not add chain.doFilter(request, response) after a Filter method Then subsequent filters and servlets will not execute. Why? After reading my handwritten Demo, you'll see

You can see that Filter can process the request before it reaches the Servlet, such as

  1. Request code
  2. Sensitive word filtering, etc

Interested partners can see the relevant source code

The implementation of handwriting Filter

Servlet interface, any web request will call the service method

public interface Servlet {
    public void service();
}
public class MyServlet implements Servlet {
    @Override
    public void service() {
        System.out.println("MyServlet Of service Method executed");
    }
}

Interceptor interface

public interface Filter {
    public void doFilter(FilterChain chain);
}
public class LogFilter implements Filter {
    @Override
    public void doFilter(FilterChain chain) {
        System.out.println("LogFilter Executed");
        chain.doFilter();
    }
}
public class ImageFilter implements Filter {
    @Override
    public void doFilter(FilterChain chain) {
        System.out.println("ImageFilter Executed");
        chain.doFilter();
    }
}

Blocker chain object

public interface FilterChain {
    public void doFilter();
}
public class ApplicationFilterChain implements FilterChain {

    private Filter[] filters = new Filter[10];
    private Servlet servlet = null;

    // Total filters
    private int n;

    // Number of currently executed filter s
    private int pos;

    @Override
    public void doFilter() {
        if (pos < n) {
            Filter curFilter = filters[pos++];
            curFilter.doFilter(this);
            return;
        }
        servlet.service();
    }

    public void addFilter(Filter filter) {
        // The source code has a dynamic capacity expansion process, similar to ArrayList
        // I won't show you how to assign the array size to 10
        filters[n++] = filter;
    }

    public void setServlet(Servlet servlet) {
        this.servlet = servlet;
    }
}

Test example

public class Main {

    public static void main(String[] args) {
        // In the tomcat source code, a request is encapsulated as an ApplicationFilterChain object
        // Then execute the doFilter method of ApplicationFilterChain
        ApplicationFilterChain applicationFilterChain = new ApplicationFilterChain();
        applicationFilterChain.addFilter(new LogFilter());
        applicationFilterChain.addFilter(new ImageFilter());
        applicationFilterChain.setServlet(new MyServlet());

        // LogFilter executed
        // ImageFilter executed
        // The service method of MyServlet is executed
        applicationFilterChain.doFilter();
    }
}

If chain.doFilter() is not added at the end of any Filter method, the interceptor and Servlet will not execute. I'm sure you can see that the doFilter method of the ApplicationFilterChain class is a simple recursive call

Spring MVC Interceptor

Use of Interceptor

I've written an article about interceptor application before. If you want to know how to use it, you can take a look at it

Security measures of web application with Spring MVC interceptor

Let's analyze how the interceptor is implemented today? Interceptors can be implemented in the following ways

  1. Implement HandlerInterceptor interface
  2. Inherit the abstract class of HandlerInterceptorAdapter and rewrite part of the implementation as needed. (HandlerInterceptorAdapter also implements HandlerInterceptor interface)

All in all, interceptors must implement the HandlerInterceptor interface

There are three methods for HandlerInterceptor

boolean preHandler(): before controller executes. After void postHandler():controller is executed, it is called before the page is rendered. void afterCompletion(): after page rendering, it is generally used for resource cleanup operations.

This graph should show a good place where requests can be intercepted

  1. Servlet Filter is to intercept the process of a request arriving at the servlet
  2. The HandlerInterceptor intercepts the request before and after the Controller's method execution when it reaches the dispatcher servlet

The implementation of handwriting Interceptor

I'll write a Demo, and you'll see it in a minute

Intercept interface, for convenience, I only define one method here

public interface HandlerInterceptor {
    boolean preHandle();
}

Two interceptors are defined as follows

public class CostInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle() {
        // Here you can do a series of things for the incoming parameters, and I will simply return true here;
        System.out.println("CostInterceptor Executed");
        return true;
    }
}
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle() {
        System.out.println("LoginInterceptor Executed");
        return true;
    }
}

Container for holding interceptors

public class HandlerExecutionChain {

    private List<HandlerInterceptor> interceptorList = new ArrayList<>();

    public void addInterceptor(HandlerInterceptor interceptor) {
        interceptorList.add(interceptor);
    }

    public boolean applyPreHandle() {
        for (int i = 0; i < interceptorList.size(); i++) {
            HandlerInterceptor interceptor = interceptorList.get(i);
            if (!interceptor.preHandle()) {
                return false;
            }
        }
        return true;
    }
}

Demonstrate the calling process of dispatcher Servlet

public class Main {

    public static void main(String[] args) {
        // Spring MVC will return a HandlerExecutionChain object upon request
        // Then execute the applyPreHandle method of the HandlerExecutionChain and the method in the controller
        HandlerExecutionChain chain = new HandlerExecutionChain();
        chain.addInterceptor(new CostInterceptor());
        chain.addInterceptor(new LoginInterceptor());

        // Only when the interceptor returns true will the method of the controller be called
        // CostInterceptor executed
        // LoginInterceptor executed
        if (!chain.applyPreHandle()) {
            return;
        }
        result();
    }

    public static void result() {
        System.out.println("This is controller Method of");
    }
}

If any of the interceptors returns false, the subsequent methods in Interceptor and Controller will not execute for obvious reasons in Demo

When you want to add new filtering logic to the request, you only need to define an interceptor, which fully conforms to the opening and closing principle.

I don't know if you realize that no Servlet Filter or Spring MVC Interceptor is implemented in the responsibility chain mode

Let's see what dispatcher servlet does? As like as two peas on demo.

When we write web applications with servlets, a request address writes a servlet class. After using spring mvc, there is only one Servlet in the whole application, that is, DispatcherServlet. All requests are sent to DispatcherServlet, and then the method of controller is executed by method call

The source code of the doDispatch method of DispatcherServlet is as follows, omitting part of the logic (all requests will execute this method)

protected void doDispatch() {

	// Execute the preHandle method of all handlerinterceptors
	if (!mappedHandler.applyPreHandle(processedRequest, response)) {
		return;
	}

	// Execute methods in controller
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	// Execute the postHandle method of all handlerinterceptors
	mappedHandler.applyPostHandle(processedRequest, response, mv);
}

The Interceptor can be used for the following purposes

  1. Record interface response time
  2. Judge whether users log in or not
  3. Authority verification, etc

You can see that both Servlet Filter and Spring MVC Interceptor can intercept requests, but at different times. And Servlet Filter is the specification of Servlet, while Spring MVC Interceptor can only be used in Spring MVC

Welcome to pay attention

Reference blog

[0]https://mp.weixin.qq.com/s/8AIRvz5HOgjw12PbsjZhCQ [1]https://www.cnblogs.com/xrq730/p/10633761.html filter source code analysis [2]https://cloud.tencent.com/developer/article/1129724 [3]https://www.jianshu.com/p/be47c9d89175

Topics: Programming Spring Google xml Tomcat