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队列的出队顺序