자바에서 필터(filter)와 인터셉터(Interceptor)의 차이점과 용도

필터 필터는 서블릿 컨테이너에 따라 다르고 인터셉터는 스프링 컨테이너에 따라 다릅니다.

  • 필터는 함수 콜백 기반으로 구현하고, 인터셉터는 자바 리플렉션 메커니즘 기반으로 구현
  • 필터는 javax.servlet.Filter를 구현하는 것이며, 인터페이스는 서블릿 사양에 의해 정의되며, 필터(필터)의 사용은 Tomcat 컨테이너와 같은 서블릿 컨테이너에 의존해야 합니다. 인터셉터는 스프링 프레임워크에서 제공하고 스프링 컨테이너에서 관리하는 org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현하며 서블릿 컨테이너가 아닌 스프링 컨테이너에 의존한다.
  • 필터(filter)는 서블릿에 도달하기 전에 요청을 전처리하는 것입니다. 인터셉터(interceptor)는 요청이 서블릿에 도달한 후 액션(컨트롤러)에 들어가기 전에 요청을 전처리하고, 액션(컨트롤러)이 처리를 마치고 뷰로 복귀하기 전에 처리하며, 액션(컨트롤러)이 서블릿으로 복귀한 후 최종 처리를 수행한다. 보다.
  • 필터는 웹 컨테이너가 관리하는 거의 모든 리소스 요청(JSP, Servlet, 정적 리소스 등)을 필터링할 수 있으며 인터셉터는 액션(SpringMVC의 컨트롤러)만 가로챌 수 있습니다.

 이 프로젝트는 springboot에서 (필터) 필터 및 (인터셉터) 인터셉터를 사용하여 springboot 프로젝트(버전 2.3.7), 관련 종속성을 생성합니다.

<?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>

필터 필터를 구현하는 방법:

사용자 정의 클래스는 세 가지 메소드를 정의하는 javax.servlet.Filter 인터페이스를 구현합니다.

//
// 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 주석 버전(@Order 주석으로 다중 필터 환경에서 우선순위를 설정할 수 있으며 숫자가 작을수록 우선순위가 높음)

1. MyFilter1, MyFilter2 및 MyFilter3 클래스를 사용자 지정하여 javax.servlet.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 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. FilterController 클래스 사용자 지정

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 기본값

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


4. 프로젝트 시작

 5. Postman 방문 http://localhost:8080/hello

 @Order 주석을 주석 처리한 후

 기본적으로 필터 체인은 필터 이름 문자를 정렬하여 실행됩니다.

2. @WebFilter+@ServletComponentScan 주석(우선순위는 설정할 수 없지만 필터링 규칙은 설정할 수 있음)

1. @Component 및 @Order를 주석 처리하고 @WebFilter 주석을 필터에 추가하고 필터 URL을 설정하고 @ServletComponentScan 주석을 시작 클래스로 설정합니다.

필터

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() {

    }
}

시작 수업

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 방문 http://localhost:8080/hello

 MyFilter2 필터는 필터링 규칙에 따라 /bye에 액세스할 때만 실행됩니다.

 MyFilter3는 일반 규칙을 설정하지 않으며 모든 요청은 기본적으로 필터링됩니다.

3. MyFilter2에서 doFilter() 메소드의 filterChain.doFilter(servletRequest, servletResponse)를 주석 처리합니다. 규칙이 삭제되고 모든 요청이 기본적으로 필터링됩니다.

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() {

    }
}

 

 

 Myfilter2에서 허용되지 않았기 때문에 MyFilter3 필터가 실행되지 않았기 때문에 요청이 실패했습니다.

3. Java 구성 클래스를 통해 필터를 등록합니다.

1. MyFilter1, MyFilter2 및 MyFilter3의 @WebFilter, @Component, @Order 주석을 주석 처리합니다.

2. FilterConfig 구성 클래스를 생성하고 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. MyFilter2#doFilter()에서 filterChain.doFilter(servletRequest,servletResponse)의 주석을 제거하고 응답 데이터 코드를 주석 처리합니다.

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. 각각 /hello 및 /bye를 방문합니다.

 이 중에서 /hello는 세 개의 필터 MyFilter3, MyFilter2 및 MyFilter1과 일치하고 /bye는 필터 MyFilter2에만 일치합니다.

Spring은 또한 문자 인코딩 필터, 교차 도메인 필터 등과 같은 일부 기본 필터 구현을 제공합니다.

 CharacterEncodingFilter는 OncePerRequestFilter에서 상속하고, OncePerRequestFilter는 GenericFilterBean에서 상속하고, GenericFilterBean은 javax.servlet.Filter를 구현하고, CharacterEncodingFilter는 설정할 수 있는 3가지 속성을 제공합니다.

인코딩: 인코딩을 설정합니다.

forceRequestEncoding: 강제 요청 인코딩. true로 설정하면 여기에 설정된 인코딩이 강제로 사용됩니다(다른 설정은 무시됨).

forceResponseEncoding: 강제 응답 인코딩, 효과는 위와 동일합니다.

인터셉터 인터셉터를 구현하는 방법:

사용자 정의 클래스는 3가지 메소드를 제공하는 org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현합니다.

//
// 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. MyInterceptor1 및 MyInterceptor2를 커스터마이즈하여 org.springframework.web.servlet.HandlerInterceptor 인터페이스의 세 가지 메소드를 구현합니다.

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. MyWebMvcConfigurer를 사용자 정의하여 org.springframework.web.servlet.config.annotation.WebMvcConfigurer 인터페이스를 구현합니다.WebMvcConfigurer는 Spring 컨테이너에서 제공하는 구성 메소드입니다.기존 XML 파일 대신 Java 구성 클래스를 사용하여 프레임워크를 사용자 정의합니다.사용자 정의 configuration 클래스는 이 인터페이스의 메소드를 구현하고 나서 HandleInterceptor, MessageConverter 등을 커스텀 구현하여 WebMvcConfigurer 커스텀 구현 클래스의 해당 레지스터에 등록하여 springMVC의 커스텀 설정을 실현한다. 제공한다

//
// 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;
    }
}

여기서는 addInterceptors(InterceptorRegistry registry) 메서드가 사용되며 InterceptorRegistry 인스턴스를 전달해야 합니다.

  • addInterceptor: HandlerInterceptor 인터페이스를 구현하고 InterceptorRegistration 개체를 반환하는 인스턴스를 전달해야 합니다.
  • InterceptorRegistration#addPathPatterns(): 인터셉터 차단 규칙을 설정하는 데 사용됩니다.
  • InterceptorRegistration#excludePathPatterns(): 차단할 필요가 없는 규칙을 설정하는 데 사용됩니다.
  • interceptorRegistration#order(): 인터셉터 우선 순위를 설정하는 데 사용되며 숫자가 작을수록 우선 순위가 높습니다.

MyWebMvcConfigurer를 사용자 지정하여 WebMvcConfigurer 인터페이스를 구현하고 addInterceptors() 메서드를 구현합니다.

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. 커스텀 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. http://localhost:8080/interceptor/hello 실행 및 액세스

 우선 순위 설정 때문에 MyInterceptor2#preHandle이 MyInterceptor1#preHandle 보다 먼저 실행되는 것을 볼 수 있지만, MyInterceptor2#postHandleMyInterceptor2#afterCompletion은 MyInterceptor1#postHandleMyInterceptor1#afterCompletion 이후 에 실행되는 것을 볼 수 있다 . ()

    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);
            }

        }
    }

HandlerExecutionChain#applyPreHandle()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);
            }
        }

    }

이 두 메서드의 순회 순서가 반대이기 때문에 preHandlepostHandle을 호출하는 순서가 반대이고 afterCompletion 도 마찬가지입니다 .

추천

출처blog.csdn.net/weixin_44341110/article/details/127446625