This article is included in personal blog: www.chengxy-nds.top, sharing technical resources, common progress

Over the weekend, a friend added me to wechat and asked me a question: “What’s the difference between a Filter and an Interceptor?” When I heard the topic, my first feeling was: “simple”!

After all, these two tools are used in the development of quite high frequency, the application is relatively simple, but when I was ready to reply to him, unexpectedly did not know where to start, hemming and hazing for a long time, scene fried chicken embarrassed, work so long a basic question answered like this, lost adults.Usually feel simple knowledge points, but usually do not pay too much attention to details, once asked by others, but can not say why.

In the final analysis, or to understand these knowledge is not enough, has been staying in the stage of use, so that now “a look will say waste”! This is typical foundation not solid performance, ah ·~, actually I also be a vanity fat!

What is the difference between the two in the end?

Prepare the environment

We configure both interceptors and filters in our project.

1. Filter

The Filter configuration is relatively simple. You can implement the Filter interface directly or use the @webFilter annotation to intercept specific urls. You can see that the Filter interface defines three methods.

  • Init () : This method is called when the container starts initializing the 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 on every request in the container, and FilterChain is used to call the next Filter, Filter.

  • Destroy () : This method is called when the container destroys the Filter instance, typically destroying or closing the resource within the method and only once during the Filter’s lifetime

@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 in process");  filterChain.doFilter(servletRequest, servletResponse);  }   @Override  public void destroy() {   System.out.println(Rear "Filter");  } } Copy the code

2. Interceptor

Interceptor it is a chain call, an application can have multiple interceptors at the same time, a request can trigger multiple interceptors, and each Interceptor invocation will be executed according to its declaration order.

First write a simple interceptor class, request interception is through the HandlerInterceptor to achieve, see the HandlerInterceptor interface also defines three methods.

  • PreHandle () : This method is called before the request is processed. “Note” : If this method returns false, the request is considered finished, and not only will its own interceptor fail, but other interceptors will also stop executing.

  • PostHandle () : only executed if the preHandle() method returns true. Is called after a method call in the Controller and before the DispatcherServlet returns to the render view. The “interesting thing” is that postHandle() methods are called in the opposite order to preHandle(). The interceptor preHandle() method declared first executes first, while postHandle() executes afterwards.

  • AfterCompletion () : Will only be executed if the preHandle() method returns true. At the end of the entire request, the DispatcherServlet renders the corresponding view and executes it.

@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 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 urls to block or exclude with attributes 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’re not the same

Filters and interceptors both embody the programming philosophy of AOP and can implement functions such as logging and login authentication, but there are many differences between the two, as explained below.

1. Different implementation principles

The underlying implementation of filters and interceptors is quite different, with filters based on function callbacks and interceptors based on Java’s reflection mechanism (dynamic proxies).

Focus on filters here!

We implement a doFilter() method in our custom filters, which takes a FilterChain argument and is actually a callback interface. ApplicationFilterChain is its implementation class, which also has a callback method called doFilter() inside.

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

ApplicationFilterChainYou can get our custom ones in therexxxFilterClass within which to call back methodsdoFilter()Call each customxxxFilterFilter and executedoFilter()Methods.

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

Each xxxFilter executes its own doFilter() logic and filterchain-dofilter (servletRequest, servletResponse) is executed at the end of the execution. The doFilter() method of ApplicationFilterChain is looped through to implement the function callback.

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

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

2. Different scope of use

We see that the filter implementsjavax.servlet.FilterInterface, and this interface is inServletDefined in the specification, that is, filtersFilterThe use of… depends onTomcatWait for the container, resulting in it only inwebUsed in the program.And interceptors (Interceptor) It’s aSpringComponent, and bySpringContainer-managed, not dependentTomcatSuch containers can be used independently. It’s not just applied towebProgram, can also be used forApplication,SwingAnd so on.

3. Different trigger times

The filterThe interceptorThe timing of the trigger varies. Let’s look at the graph below.

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

The Interceptor preprocesses the request after it enters the servlet, but before it enters the Controller, where it renders the corresponding view.

4. The scope of intercepted requests is different

Now that we’ve configured both filters and interceptors, create a Controller to receive requests and test it.

@Controller
@RequestMapping()
public class Test {

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

The filter was found during project startupinit()Method is initialized with the start of the container.When the browser sends a request, F12 sees two requests, one of which is our ownControllerRequest, and the other is a request to access a static icon resource.The console logs are as follows:

It is implemented in the following order: Filter > Interceptor; I am controller > Interceptor

The Filter processingInterceptor pre -In dealing with the InterceptorRear InterceptorThe Filter processing Copy the code

The Filter is executed twice, and the Interceptor only once. This is because filters work on almost all requests coming into the container, whereas interceptors only work on requests in the Controller or requests to access resources in the static directory.

5. Injection beans are different

In real business scenarios, applying filters or interceptors will inevitably introduce some services to process business logic.

Add a service to the filter and to the interceptor.

@Component
public class TestServiceImpl implements TestService {

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

Inject the service into the filter and test the request. The log displays “I am method A”.

@Autowired
    private TestService testService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  System.out.println("Filter in process");  testService.a();  filterChain.doFilter(servletRequest, servletResponse);  } Copy the code
The Filter processingI'm method AInterceptor pre -I am the controllerIn dealing with the InterceptorRear InterceptorCopy the code

Inject in the interceptorserviceTo test the request, TM reported an error.debugFollow up with the discovery injectedserviceHow is theNullAh?This is because of the loading order,The interceptorThe loading point is atspringcontextBefore,BeanAnd by thespringManage.

Interceptor: Lao Tzu will enter the bridal chamber today; Spring: Don’t be ridiculous, brother. I haven’t even had your daughter-in-law yet.

The solution is simple: inject the Interceptor manually before registering the Interceptor. “Note” : An instance of getMyInterceptor() is registered in Registry.addInterceptor ().

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
 System.out.println("Injected MyInterceptor");  return new MyInterceptor();  }   @Override  public void addInterceptors(InterceptorRegistry registry) {   registry.addInterceptor(getMyInterceptor()).addPathPatterns("/ * *");  } } Copy the code

6. Control execution sequence is different

In real development, multiple filters or interceptors may exist at the same time, but there are times when we want one filter or interceptor to be executed first, and the order in which they are executed is involved.

Filters use the @ORDER annotation to control the Order of execution. Filters use the @order annotation to control the level of the filter. The lower the value, the higher the level, the earlier the execution.

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

The default execution Order of the interceptor, which is the Order in which it was registered, can also be manually controlled by Order, with smaller values being 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 interceptor preHandle() method declared first executes first, while the postHandle() method executes afterwards.

PostHandle () is called in the opposite order to preHandle(). This is especially important if the order of execution is strictly required in actual development.

Interceptor1 pre -Interceptor2 pre -Interceptor pre -I am the controllerIn dealing with the InterceptorInterceptor2 processingInterceptor1 processingRear InterceptorInterceptor2 after processingInterceptor1 after processing Copy the code

“So why is that?” All requests in the Controller are routed through the core DispatcherServlet component, which executes its doDispatch() method. The interceptor postHandle() and preHandle() methods are called from there.

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
        try {
.            try {
  // Get the adapter that can execute the current Handler  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;  }  }  // Call the Interceptor PreHandle() method  if(! mappedHandler.applyPreHandle(processedRequest, response)) { return;  }   // Execute Handle (including our business logic, which will be tried and caught when an exception is thrown)  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   if (asyncManager.isConcurrentHandlingStarted()) {  return;  }  applyDefaultViewName(processedRequest, mv);   // When Interceptor is intercepted, PostHandle is executed.  mappedHandler.applyPostHandle(processedRequest, response, mv);  }  } . }  Copy the code

To see how applyPreHandle() and applyPostHandle() are called, 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

Found two methods calling an interceptor arrayHandlerInterceptor[]When, the order of the cycle is actually reversed… , resulting inpostHandle(),preHandle()Methods are executed 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.


The original is not easy, “burning hair output content”, if you have a lost harvest, point a “like” to encourage oh!

Sorted out hundreds of technical e-books and sent them to my friends. Pay attention to the public number reply [” 666 “] to get by yourself. I set up a technology exchange group with some friends to discuss technology and share technical information, aiming to learn and progress together. If you are interested, please join us!