The difference and use of filter (filter) and interceptor (Interceptor) in Java

The filter filter depends on the servlet container, and the interceptor depends on the spring container

  • Filter is implemented based on function callback, and interceptor is implemented based on Java reflection mechanism
  • filter is to implement javax.servlet.Filter, the interface is defined by the servlet specification, the use of filter (filter) needs to depend on the servlet container, such as the Tomcat container. The interceptor implements the org.springframework.web.servlet.HandlerInterceptor interface, which is provided by the spring framework and managed by the spring container. It does not depend on the servlet container, but on the spring container.
  • Filter (filter) is to preprocess the request before it reaches the servlet. The interceptor (interceptor) preprocesses the request before entering the action (controller) after it reaches the servlet, processes it before the action (controller) finishes processing and returns to the view, and performs final processing after the action (controller) returns to the view.
  • The filter can filter almost all resource requests (JSP, Servlet, static resources, etc.) managed by the web container, and the interceptor can only intercept the action (controller in SpringMVC).

 This project uses (filter) filter and (interceptor) interceptor in springboot to create a springboot project (version 2.3.7), related dependencies:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.example.demo.DemoApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

The way to implement the filter filter:

The custom class implements the javax.servlet.Filter interface, which defines three methods

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.io.IOException;

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }
    //主要实现此方法即可,该方法最终调用filterChain.doFilter(servletRequest,servletResponse)则放行,不调用则拦截
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}

1. @Component annotation version (with the @Order annotation, the priority can be set in a multi-filter environment, the smaller the number, the higher the priority)

1. Customize the MyFilter1, MyFilter2, and MyFilter3 classes to implement the javax.servlet.Filter interface

package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */

@Component
@Order(3)
public class MyFilter1 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 1");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 1");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}
package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */

@Component
@Order(2)
public class MyFilter2 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 2");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 2");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}
package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */

@Component
@Order(1)
public class MyFilter3 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 3");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 3");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

2. Customize the FilterController class

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author jiang
 * @date 2022/10/19 16:05
 */

@RestController
public class FilterController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @RequestMapping("/hello")
    public String hello(){
        String str = "hello";
        logger.info(str);
        return str;
    }
    @RequestMapping("/bye")
    public String bye(){
        String str = "bye";
        logger.info(str);
        return str;
    }
}

3. application.properties default

# 应用名称
spring.application.name=demo
# 应用服务 WEB 访问端口
server.port=8080


4. Start the project

 5. Postman visits http://localhost:8080/hello

 After commenting out the @Order annotation

 By default, the filter chain is executed by sorting the filter name characters

2. @WebFilter+@ServletComponentScan annotation (priority cannot be set, but filtering rules can be set)

1. Comment out @Component and @Order, add the @WebFilter annotation to the filter and set the filter URL, and set the @ServletComponentScan annotation to the startup class

Filter

package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */

@WebFilter(urlPatterns = "/hello")
//@Component
//@Order(3)
public class MyFilter1 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 1");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 1");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}
package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */
@WebFilter(urlPatterns = "/bye")
//@Component
//@Order(2)
public class MyFilter2 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 2");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 2");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}
package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */
@WebFilter
//@Component
//@Order(1)
public class MyFilter3 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 3");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 3");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

startup class

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan//扫描servlet提供注解
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

2. Postman visits http://localhost:8080/hello

 The MyFilter2 filter is only executed when accessing /bye according to the filtering rules

 MyFilter3 does not set regular rules, and all requests are filtered by default

3. Comment out the filterChain.doFilter(servletRequest, servletResponse) of the doFilter() method in MyFilter2; that is, the interrupt request will not be released, and the data will be returned to the user through ServletResponse, and the @WebFilter filter rule will be deleted, and all requests will be filtered by default

package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */
@WebFilter
//@Component
//@Order(2)
public class MyFilter2 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 2");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 2");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
//        filterChain.doFilter(servletRequest,servletResponse);
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        httpServletResponse.setCharacterEncoding("UTF-8");
        PrintWriter pw = httpServletResponse.getWriter();

        pw.write("请求未通过");
        pw.flush();
        pw.close();

    }

    @Override
    public void destroy() {

    }
}

 

 

 The request failed because the MyFilter3 filter was not executed because it was not allowed in Myfilter2

3. Register the filter through the Java configuration class

1. Comment out the @WebFilter, @Component, @Order annotations of MyFilter1, MyFilter2, and MyFilter3

2. Create a FilterConfig configuration class, and set the priority and filter rules through FilterRegistrationBean

package com.example.demo.config;

import com.example.demo.filter.MyFilter1;
import com.example.demo.filter.MyFilter2;
import com.example.demo.filter.MyFilter3;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author jiang
 * @date 2022/10/21 17:03
 */

@Configuration
public class FilterConfig {

    /*
    * 若只配置自定义过滤器Bean,则执行顺序按照配置先后顺序执行,即2--》3--》1
    * */
    @Bean
    public MyFilter2 getMyFilter2(){
        return new MyFilter2();
    }
    @Bean
    public MyFilter3 getMyFilter3(){
        return new MyFilter3();
    }
    @Bean
    public MyFilter1 getMyFilter1(){
        return new MyFilter1();
    }

    /*
    * 若配置了FilterRegistrationBean注入自定义过滤器,则按照FilterRegistrationBean配置顺序执行过滤器,即1--》3--》2
    * */
    @Bean
    public FilterRegistrationBean getFilterRegistrationBean1(MyFilter1 myFilter1){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(myFilter1);
        //filterRegistrationBean对象提供方法设置优先级,则以此为准,数字越小,优先级越高,当设置数字相同时,依然按照配置顺序执行
        filterRegistrationBean.setOrder(3);
        //设置过滤URL
        filterRegistrationBean.addUrlPatterns("/hello");
        return filterRegistrationBean;
    }
    @Bean
    public FilterRegistrationBean getFilterRegistrationBean3(MyFilter3 myFilter3){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(myFilter3);
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/hello");
        return filterRegistrationBean;
    }
    @Bean
    public FilterRegistrationBean getFilterRegistrationBean2(MyFilter2 myFilter2){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(myFilter2);
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }


}

 3. Uncomment filterChain.doFilter(servletRequest,servletResponse) in MyFilter2#doFilter(); and comment out the response data code

package com.example.demo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author jiang
 * @date 2022/10/19 16:07
 */
//@WebFilter
//@Component
//@Order(2)
public class MyFilter2 implements Filter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("过滤器初始化完成 2");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //对请求进行预处理,如权限处理、IP过滤、敏感词过滤、跨域处理等等
        logger.info("doFilter 2");
        //预处理完成后判断是否允许放行,调用filterChain.doFilter()方法会回调过滤链里所有的过滤器,
        // 所有过滤器处理完并调用此方法后请求会到达servlet,
        // 如果任一过滤器未调用此方法都会导致请求无法到达servlet,即请求被拦截,shiro权限控制框架即是通过此接口实现
        filterChain.doFilter(servletRequest,servletResponse);
//        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
//        httpServletResponse.setCharacterEncoding("UTF-8");
//        PrintWriter pw = httpServletResponse.getWriter();
//
//        pw.write("请求未通过");
//        pw.flush();
//        pw.close();

    }

    @Override
    public void destroy() {

    }
}

4. Visit /hello and /bye respectively

 Among them, /hello matches the three filters MyFilter3, MyFilter2, and MyFilter1, and /bye only matches the filter MyFilter2.

Spring also provides some default filter implementations, such as character encoding filters, cross-domain filters, etc.

 CharacterEncodingFilter inherits from OncePerRequestFilter, OncePerRequestFilter inherits from GenericFilterBean, GenericFilterBean implements javax.servlet.Filter, CharacterEncodingFilter provides 3 properties that can be set:

encoding: set the encoding.

forceRequestEncoding: Force request encoding, if set to true, the encoding set here is forced to be used (other settings are overridden).

forceResponseEncoding: force response encoding, the effect is the same as above.

The method of implementing the interceptor interceptor:

The custom class implements the org.springframework.web.servlet.HandlerInterceptor interface, which provides 3 methods

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerInterceptor {
    //根据匹配规则拦截action(springMVC里的Controller),在请求到达action之前调用,返回true则请求到达action,返回false则拦截请求
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    //在action处理完后返回前调用
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
    //在action处理完并返回响应之后调用
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

1. Customize MyInterceptor1 and MyInterceptor2 to implement three methods of org.springframework.web.servlet.HandlerInterceptor interface

package com.example.demo.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author jiang
 * @date 2022/10/21 15:38
 */

@Component
public class MyInterceptor1 implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    //请求到达action(controller)之前执行,返回true则放行,请求到达action,否则拦截
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("preHandle 1");
        return true;
    }
    //action处理请求后return前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("postHandle 1");

    }
    //action处理完成return后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("afterCompletion 1");
    }
}
package com.example.demo.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author jiang
 * @date 2022/10/22 16:06
 */
@Component
public class MyInterceptor2 implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("preHandle 2");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("postHandle 2");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("afterCompletion 2");
    }
}

2. Customize MyWebMvcConfigurer to implement the org.springframework.web.servlet.config.annotation.WebMvcConfigurer interface. WebMvcConfigurer is a configuration method provided by the spring container. It uses Java configuration classes instead of traditional XML files to customize the framework. The custom configuration class implements the method of this interface, and then custom implements some such as HandleInterceptor, MessageConverter, etc. to register in the relevant register of the WebMvcConfigurer custom implementation class to realize the custom configuration of springMVC. First, let's see what methods the interface provides

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet.config.annotation;

import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;

public interface WebMvcConfigurer {
    //配置路径匹配
    default void configurePathMatch(PathMatchConfigurer configurer) {
    }
    //配置内容裁决的参数
    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }

    default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }
    //配置默认静态资源处理
    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }

    default void addFormatters(FormatterRegistry registry) {
    }
    //配置拦截器
    default void addInterceptors(InterceptorRegistry registry) {
    }
    //配置静态资源处理
    default void addResourceHandlers(ResourceHandlerRegistry registry) {
    }
    //跨域设置
    default void addCorsMappings(CorsRegistry registry) {
    }
    //配置视图控制器
    default void addViewControllers(ViewControllerRegistry registry) {
    }
    //配置视图解析器
    default void configureViewResolvers(ViewResolverRegistry registry) {
    }
    //配置参数解析器
    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    }
    //配置返回值处理
    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
    }
    //配置信息转换器
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    @Nullable
    default Validator getValidator() {
        return null;
    }
    
    @Nullable
    default MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}

The addInterceptors(InterceptorRegistry registry) method is used here, and the InterceptorRegistry instance needs to be passed in:

  • addInterceptor: You need to pass in an instance that implements the HandlerInterceptor interface and return an InterceptorRegistration object.
  • InterceptorRegistration#addPathPatterns(): Used to set interceptor interception rules.
  • InterceptorRegistration#excludePathPatterns(): Used to set rules that do not need to be intercepted.
  • interceptorRegistration#order(): Used to set the interceptor priority, the smaller the number, the higher the priority.

Customize MyWebMvcConfigurer to implement the WebMvcConfigurer interface and implement the addInterceptors() method

package com.example.demo.config;

import com.example.demo.interceptor.MyInterceptor1;
import com.example.demo.interceptor.MyInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


/**
 * @author jiang
 * @date 2022/10/22 13:56
 */
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    @Autowired
    private  MyInterceptor1 myInterceptor1;
    @Autowired
    private MyInterceptor2 myInterceptor2;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注入2个拦截器,并设置优先级
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(myInterceptor1);
        interceptorRegistration.addPathPatterns("/interceptor/*").order(2);
        registry.addInterceptor(myInterceptor2).addPathPatterns("/interceptor/*").order(1);
    }
}

3. Custom InterceptorController

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author jiang
 * @date 2022/10/22 16:39
 */
@RestController
@RequestMapping("/interceptor")
public class InterceptorController {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping("/hello")
    public String hello(){
        String str = "hello";
        logger.info(str);
        return str;
    }
    @RequestMapping("/bye")
    public String bye(){
        String str = "bye";
        logger.info(str);
        return str;
    }

}

4. Run and access http://localhost:8080/interceptor/hello

 It can be seen that MyInterceptor2#preHandle is executed before MyInterceptor1#preHandle because of the priority setting , but MyInterceptor2#postHandle and MyInterceptor2#afterCompletion are executed after MyInterceptor1#postHandle and MyInterceptor1#afterCompletion . The reason is to call the source code DispatcherServlet#doDispatch()

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //获取处理器执行链,返回值是HandlerExecutionChain对象
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    //处理器执行链调用拦截器的preHandle()
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    //处理器执行链调用拦截器的postHandle()
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

Look at HandlerExecutionChain#applyPreHandle() and HandlerExecutionChain#applyPostHandle()

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            //遍历执行拦截器preHandle()
            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;
    }

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            //遍历执行拦截器postHandle(),但是与preHandle()遍历顺序相反
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }

    }

Because the traversal order of these two methods is opposite, the order of calling preHandle and postHandle is reversed, and the same is true for afterCompletion .

Guess you like

Origin blog.csdn.net/weixin_44341110/article/details/127446625