Java里过滤器(filter)与拦截器(Interceptor)的区别和使用

filter过滤器依赖于servlet容器,而interceptor依赖于spring容器

  • filter是基于函数回调实现的,interceptor是基于Java反射机制实现的
  • filter是实现javax.servlet.Filter,该接口由servlet规范定义,使用过滤器(filter)需要依赖于servlet容器,如Tomcat容器。而interceptor是实现org.springframework.web.servlet.HandlerInterceptor接口,由spring框架提供且由spring容器管理,不依赖servlet容器,依赖spring容器。
  • filter(过滤器)是在请求到达servlet之前进行预处理。interceptor(拦截器)是在请求到达servlet之后在进入action(controller)之前进行预处理,在action(controller)处理完返回视图前再处理,待action(controller)返回视图后进行最终处理。
  • filter可以过滤被web容器管理的几乎所有资源请求(JSP、Servlet、静态资源等),interceptor只能对action(SpringMVC里的controller)进行拦截处理。

 本项目在springboot里使用(filter)过滤器和(interceptor)拦截器,创建一个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>

实现filter过滤器的方法:

自定义类实现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() {
    }
}

一、@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注解后

 默认按照过滤器名称字符排序来执行过滤链

二、@WebFilter+@ServletComponentScan注解(无法设置优先级,但是可以设置过滤规则)

1、注释掉@Component和@Order,给过滤器添加@WebFilter注解并设置过滤URL,给启动类设置@ServletComponentScan注解

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

    }
}

启动类

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

 根据过滤规则访问/bye时才执行MyFilter2过滤器

 MyFilter3未设置规律规则,默认所有请求都过滤

3、注释掉MyFilter2里doFilter()方法的filterChain.doFilter(servletRequest,servletResponse);即中断请求不放行,通过ServletResponse返回数据给用户,将@WebFilter过滤规则删除,默认所有请求过滤

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过滤器

三、通过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个属性可设置:

encoding:设置编码。

forceRequestEncoding:强制请求编码,若设置true,则强制使用此处设置的编码(其他地方设置被覆盖)。

forceResponseEncoding:强制响应编码,效果同上。

实现interceptor拦截器的方法:

自定义类实现org.springframework.web.servlet.HandlerInterceptor接口,该接口提供3个方法

//
// 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容器提供的一种配置方式,采用Java配置类形式代替传统的XML文件对框架进行自定义设置,可以自定义配置类实现该接口的方法,然后自定义实现一些如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#preHandleMyInterceptor1#preHandle先执行,但是MyInterceptor2#postHandleMyInterceptor2#afterCompletionMyInterceptor1#postHandleMyInterceptor1#afterCompletion之后执行,原因看调用源码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);
            }

        }
    }

再看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也是同理。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值