Filter 是 JavaEE 中 Servlet 规范的一个组件,位于包javax.servlet 中,它可以在 HTTP 请求到达 Servlet 之前,被一个或多个Filter处理。

1.案例结构

1.1first拦截器

@WebFilter(filterName = "first" , urlPatterns = "/*")
@Order(1)
public class FirstFilter implements Filter {


  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    Filter.super.init(filterConfig);
    System.out.println("first拦截器初始化");
  }

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

    HttpServletRequest req = (HttpServletRequest) servletRequest;
    System.out.println("first拦截器获取Attribute"+req.getAttribute("name"));//无

    req.setAttribute("name","第一次设置的name");//作用范围为:一次请求域,即下次Filter也能取的到

    HttpServletResponse res = (HttpServletResponse) servletResponse;
    res.setHeader("firstHeader","firstHeader");//设置请求头,会一直层层传递到控制层的响应头

    filterChain.doFilter(req,res);//按Order优先顺序调用下一个Filter,直到Controller层

    System.out.println("firstFilterEnd");//doFilter内部执行完毕之后,才回来执行这个,类似二叉树的前序遍历

  }

  @Override
  public void destroy() {
    Filter.super.destroy();
    System.out.println("first拦截器销毁");
  }
}

1.2second拦截器

@WebFilter(urlPatterns = "/*")
@Order(2)
public class SecondFilter implements Filter {


  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    Filter.super.init(filterConfig);
    System.out.println("second拦截器初始化");
  }

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

    HttpServletRequest req = (HttpServletRequest) servletRequest;
    System.out.println("second拦截器获取Attribute"+req.getAttribute("name"));//第一次设置的name

    req.setAttribute("name","第二次设置的name");
    HttpServletResponse res = (HttpServletResponse) servletResponse;
    res.setHeader("secondHeader","secondeHeader");

    filterChain.doFilter(req,res);//后续没有Filter,调用Controller

    System.out.println("secondFilterEnd");

  }

  @Override
  public void destroy() {
    Filter.super.destroy();
    System.out.println("second拦截器销毁");
  }
}

1.3控制层

  @PostMapping("/web")
  public DtoRes webTest(HttpServletRequest request  ,  HttpServletResponse response ,@RequestParam("userName") String userName) throws IOException {

    System.out.println("controller");


    return null;

  }

拦截器执行流程

启动SpringBoot

firstFilter的@Order更小,优先加载

请求接口

关闭SpringBoot

2.主要注解

@WebFilter

  • 无需在web.xml 中注册一个 Filter 来对某个 Servlet 程序进行拦截处理
  • @WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
  • 最重要的就是urlPatterns拦截规则,支持正则,指明需要拦截的路径,多层拦截没有特殊情况

@Order()

@Order(1)的拦截器优先于@Order(2),默认值为@Order(2147483647)

@ServletComponentScan

  • 在SpringBootApplication(启动类)上使用
  • @ServletComponentScan注解后,Servlet、Filter(过滤器)、Listener(监听器)可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码!

3.Filter的方法

初始化init

  • 伴随Springboot启动而初始化,只初始化一次

  • 会传递一个包含 Filter 的配置和运行环境信息的 FilterConfig 对象。

  • 如果初始化代码要使用到 FilterConfig 对象,这些代码只能在 init 方法中编写,而不能在构造方法中编写(尚未调用 init 方法,即并没有创建 FilterConfig 对象,要使用它则必然出错)。

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
      Filter.super.init(filterConfig);
    }
    

进入下一Filter(直至Controller层)

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

    
    filterChain.doFilter(req,res);


  }

销毁destroy

结束SpringBoot程序时才销毁(不是结束请求)

  @Override
  public void destroy() {
    Filter.super.destroy();
  }

4.FilterChain拦截器链

  • 最后一个filterChain.doFilter(req,res);方法中调用的 FilterChain.doFilter 方法将激活目标 Servlet的service 方法。
  • 只要 Filter 链中任意一个 Filter 没有调用 filterChain.doFilter() 方法,则目标 Servlet 的 service 方法都不会被执行。
  • filterChain.doFilter(req,res);会自动把FilterChain对象传给下一个Filter.doFilter()

5.FilterConfig拦截器配置接口

  • 实现Filter接口后init()方法的形参就是FilterConfig
    摘自菜鸟

6.FilterRegistrationBean和@WebFilter区别

有2种方式可以实现过滤器

1:通过FilterRegistrationBean实例注册

2:通过@WebFilter注解生效

这里选择第一种,因为第二种不能设置过滤器之间的优先级

转载自灰信:
区别

7.OncePerRequestFilter

  • OncePerRequestFilter仍然是需要注入到Spring中,仍然通常采用@WebFilter
  • 对目标拦截路径的资源仍然正常拦截
  • OncePerRequestFilter存在于spring-web模块中org.springframework.web.filter ; 而Filter则在Servlet模块中 javax.servlet
  • OncePerRequestFilter是对Filter的扩展,他内部已重写了init() doFilter() destory(),并且还有自己的抽象方法doFilterInternal需要实现类去重写
  • 内部跳转只拦截一次(一次外部请求只过滤一次)
  • 在SpringMVC中对于Filter的扩展都是继承了OncePerRequestFilter。其中都是实现了doFilterInternal()的方法扩展。

重写的doFilter

源码中的链路调用,调用了抽象方法doFilterInternal,因此需要自己重写才能完成doFilter()的完整逻辑

doFilterInternal抽象方法

用法其实和普通的Filter差不多,但是在接口内部跳转(不加@ResponseBody返回String的接口)时,不会再进行第二次的拦截

@WebFilter(urlPatterns = "/*")
@Order(-1)
public class OnceFilter extends OncePerRequestFilter {

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {


    System.out.println("开始");

    filterChain.doFilter(request,response);

    System.out.println("结束");

  }
}

OncePerRequestFilter执行顺序

  • 当定义了多个Filter时,OncePerRequestFilter总是在第一个Filter之后执行
  • 多个OncePerRequestFilter之间也可以有@Order指定顺序
  • 所有OncePerRequestFilter按顺序执行完毕之后,才执行第二个Filter
  • OncePerRequestFilter也满足Filter队列的出队顺序

其他参考:
添加链接描述
菜鸟

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐