Cuthbert's Blog

Using Filters in the Spring Boot Framework

Published on
Published on
/599 字 · 2 分钟/---

概念

Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Sevlet 程序、Filter 过滤器、Listener 监听器。Filter 过滤器它是 JavaEE 的规范,也就是接口。它的作用是:拦截请求、过滤响应。它的常见应用场景有:认证权限检查、日志记录等等。

工作流程

image-20211029161427622

使用步骤

我们现在基本都是使用 Spring Boot 来进行 Web 开发,使用 Servlet 的比较少了。我们就在 Spring Boot 环境下进行演示了。假设现在需要实现一个记录接口运行时间的需求。实现如下: ​

@RestController
public class TestController {
 
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
 
    @RequestMapping(value = "/test")
    public void test() throws InterruptedException {
        Thread.sleep(5000);
        logger.info("访问成功");
    }
}

编写一个 controller 请求测试

public class RecordFilter implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(RecordFilter.class);
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("进入RecordFilter");
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        logger.info("执行接口耗时: {}", (System.currentTimeMillis() - start));
    }
 
}

编写一个类,实现 Filter 接口,实现过滤方法 doFilter(),配置自定义 Filter 的拦截路径,我们在 JSP 时代是在 web.xml 中去配置,但是 Spring Boot 项目并没有 web.xml 这个文件,我们需要 FilterRegistrationBean 来完成配置。实现如下:

@Configuration
public class FilterConfig {
 
    @Bean
    public FilterRegistrationBean registerFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        // 设置过滤器
        registration.setFilter(new RecordFilter());
        // 配置拦截路径
        registration.addUrlPatterns("/*");
        // 设置过滤器名称
        registration.setName("RecordFilter");
        // 设置执行顺序
        registration.setOrder(1);
        return registration;
    }
 
}

这个和 web.xml 没什么区别,只是形式不同。当然我们也可以选择基于注解方式进行配置,这个只需在我们刚刚自定义的过滤器类名上面加即可,不需要再新建一个类。这两种方式你可以任意选择一种即可。 ​

@Order(1)
@WebFilter(filterName="recordFilter", urlPatterns="/*")
@Configuration
public class RecordFilter implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(RecordFilter.class);
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("进入RecordFilter");
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        logger.info("执行接口耗时: {}", (System.currentTimeMillis() - start));
    }
 
}

image-20211029161527519

生命周期

Filter 生命周期包含一下几个方法 ​

  1. 构造器方法
  2. init 初始化方法:Web 项目启动时执行
  3. doFilter 方法:拦截到请求执行
  4. destory 销毁:Web 项目停止时执行

FilterConfig 类

FilterConfig 类是过滤器的配置文件类。Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。 ​

FilterConfig 类的作用是获取 filter 过滤器的配置内容 ​

  1. 获取 Filter 的名称
  2. 过去 Filter 中配置的初始化参数
  3. 获取 ServletContext 对象

FilterChain 过滤器链

FilterChain 就是过滤器链,多个过滤器一起工作,我们常用的认证授权框架 Spring Security 就是基于过滤器链实现的。下面演示多个过滤器的执行步骤 ​

@Order(1)
@WebFilter(filterName="filterA", urlPatterns="/*")
@Configuration
public class FilterA implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(FilterA.class);
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("进入FilterA开始");
        filterChain.doFilter(servletRequest, servletResponse);
        logger.info("进入FilterA结束");
    }
 
}
@Order(2)
@WebFilter(filterName="filterB", urlPatterns="/*")
@Configuration
public class FilterB implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(FilterB.class);
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("进入FilterB开始");
        filterChain.doFilter(servletRequest, servletResponse);
        logger.info("进入FilterB结束");
    }
 
}
@RestController
public class TestController {
 
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
 
    @RequestMapping(value = "/test")
    public void test() throws InterruptedException {
        logger.info("访问成功");
    }
}

访问日志

image-20211029161557172

由此我们可以看出多个过滤器的工作流程如下:

image-20211029161624771

filterChain.doFilter()方法的作用

  1. 如果还存在过滤器,执行下一个过滤器;
  2. 如果不存在过滤器,执行目标资源;

多个 Filter 过滤器执行的特点 ​

  1. 所有 Filter 和目标资源都执行在同一个线程中;
  2. 多个 Filter 共同执行的时候,它们都使用同一个 request 对象;