web三大组件之一Filter,能够说是不少小伙伴学习java web时最先接触的知识点了,然而学得早不表明就用得多。基本上,若是不是让你从0到1写一个web应用(或者说即使从0到1写一个web应用),在你的平常业务开发中不太可能碰到须要手写Filter的场景java
本文将简单介绍写什么是Filter,以及在SpringBoot中使用Filter的通常姿式与常见问题git
原文查看: SpringBoot系列教程web篇之过滤器Filter使用指南github
在正式开始以前,有必要先简单看一下什么是Filter(过滤器),以及这个有什么用web
Filter,过滤器,属于Servlet规范,并非Spring独有的。其做用从命名上也能够看出一二,拦截一个请求,作一些业务逻辑操做,而后能够决定请求是否能够继续往下分发,落到其余的Filter或者对应的Servletspring
简单描述下一个http请求过来以后,一个Filter的工做流程:apache
插播一句:上面这个过程,和AOP中的
@Around
环绕切面的做用差很少json
接下来咱们搭建一个web应用方便后续的演示,借助SpringBoot搭建一个web应用属于比较简单的活;数组
建立一个maven项目,pom文件以下websocket
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7</version>
<relativePath/> <!-- lookup parent from update -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
复制代码
在SpringBoot项目中,若是须要自定义一个Filter,并无什么特殊的地方,直接实现接口便可,好比下面一个输出请求日志的拦截器网络
@Slf4j
@WebFilter
public class ReqFilter implements Filter {
public ReqFilter() {
System.out.println("init reqFilter");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
log.info("url={}, params={}", req.getRequestURI(), JSON.toJSONString(req.getParameterMap()));
chain.doFilter(req, response);
}
@Override
public void destroy() {
}
}
复制代码
实现一个自定义的Filter容易,通常有两个步骤
doFilter
方法中添加业务逻辑,若是容许访问继续,则执行chain.doFilter(req, response);
; 不执行上面这一句,则访问到此为止接下来的一个问题就是如何让咱们自定义的Filter生效,在SpringBoot项目中,有两种常见的使用方式
FilterRegistrationBean
这个注解属于Servlet3+,与Spring也没有什么关系,因此问题来了,当我在Filter上添加了这个注解以后,Spring怎么让它生效呢?
@ServletComponentScan
@ServletComponentScan
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
复制代码
WebFilter经常使用属性以下,其中urlPatterns
最为经常使用,表示这个filter适用于哪些url请求(默认场景下所有请求都被拦截)
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定过滤器的 name 属性,等价于 |
value | String[] | 该属性等价于 urlPatterns 属性。可是二者不该该同时使用。 |
urlPatterns | String[] | 指定一组过滤器的 URL 匹配模式。等价于 标签。 |
servletNames | String[] | 指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 的取值。 |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。 |
initParams | WebInitParam[] | 指定一组过滤器初始化参数,等价于 标签。 |
asyncSupported | boolean | 声明过滤器是否支持异步操做模式,等价于 标签。 |
description | String | 该过滤器的描述信息,等价于 标签。 |
displayName | String | 该过滤器的显示名,一般配合工具使用,等价于 标签。 |
上面一种方式比较简单,后面会说到有个小问题,指定Filter的优先级比较麻烦,
下面是使用包装bean注册方式
@Bean
public FilterRegistrationBean<OrderFilter> orderFilter() {
FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
filter.setName("reqFilter");
filter.setFilter(new ReqFilter());
// 指定优先级
filter.setOrder(-1);
return filter;
}
复制代码
上面整完,就能够开始测试使用过滤器了,在进入实测环节以前,先来看两个常见的问题
若是有小伙伴使用SpringMVC + web.xml方式来定义Filter,就会发现自定义的Filter中没法经过@Autowired
方式来注入Spring的bean
我以前使用的是spring4 Servlet2+ ,存在上面的问题,若是有不一样观点请留言告诉我,感谢
SpringBoot中能够直接注入依赖的Bean,从上面的第二种注册方式能够看到,Spring将Filter封装成了一个Bean对象,所以能够直接注入依赖的Bean
下面定义一个AuthFilter
,依赖了自定义的DemoBean
@Data
@Component
public class DemoBean {
private long time;
public DemoBean() {
time = System.currentTimeMillis();
}
public void show() {
System.out.println("demo bean!!! " + time);
}
}
@Slf4j
@WebFilter
public class AuthFilter implements Filter {
@Autowired
private DemoBean demoBean;
public AuthFilter() {
System.out.println("init autFilter");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("in auth filter! {}", demoBean);
// 测试,用header中的 tx-demo 来判断是否为认证的请求
HttpServletRequest req = (HttpServletRequest) request;
String auth = req.getHeader("tx-demo");
if ("yihuihui".equals(auth)) {
// 只有认证的请求才容许访问,请求头中没有这个时,不执行下面的的方法,则表示请求被过滤了
// 在测试优先级时打开下面的注释
// chain.doFilter(request, response);
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
复制代码
Filter的优先级指定,经过个人实际测试,@Order
注解没有用,继承 Ordered
接口也没有用,再不考虑web.xml的场景下,只能经过在注册Bean的时候指定优先级
实例以下,三个Filter,两个经过@WebFilter
注解方式注册,一个经过FilterRegistrationBean
方式注册
@Slf4j
@Order(2)
@WebFilter
public class AuthFilter implements Filter, Ordered {
...
}
@Slf4j
@Order(1)
@WebFilter
public class ReqFilter implements Filter, Ordered {
...
}
@Slf4j
public class OrderFilter implements Filter {
}
@ServletComponentScan
@SpringBootApplication
public class Application {
@Bean
public FilterRegistrationBean<OrderFilter> orderFilter() {
FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
filter.setName("orderFilter");
filter.setFilter(new OrderFilter());
filter.setOrder(-1);
return filter;
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
复制代码
上面定义了三个Filter,咱们主要验证下优先级,若是@Order
注解生效,那么执行的前后顺序应该是
OrderFilter -> ReqFilter -> AuthFilter
若是不是上面的顺序,那么说明@Order
注解没有用
@RestController
public class IndexRest {
@GetMapping(path = {"/", "index"})
public String hello(String name) {
return "hello " + name;
}
}
复制代码
(上文截图源码来自: org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
)
上面是测试时关键链路的断点截图,从数组中能够看出 AuthFilter
的优先级大于ReqFilter
, 下面实际的输出也说明了@Order
注解不能指定Filter的优先级(不知道为何网络上有大量使用Order来指定Filer优先级的文章!!!)
接下来咱们的问题就是WebFilter
注解来注册的Filter的优先级是怎样的呢,咱们依然经过debug来看,关键代码路径为: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
2147483647
,相同优先级的状况下,根据名字前后顺序来决定本文主要介绍了过滤器Filter的使用方式,以及常见的两个问题解答,文中内容穿插了一点源码的分析截图,并未深刻,若有兴趣的同窗能够根据文中提的几个关键位置探索一番
下面简单小结下文中内容
自定义Filter的实现
chain.doFilter(request, response);
表示请求继续;不然表示请求被过滤注册生效
@ServletComponentScan
自动扫描带有@WebFilter
注解的FilterFilterRegistrationBean
来包装自定义的Filter在SpringBoot中Filter能够和通常的Bean同样使用,直接经过Autowired
注入其依赖的Spring Bean对象
经过建立FilterRegistrationBean
的时候指定优先级,以下
@Bean
public FilterRegistrationBean<OrderFilter> orderFilter() {
FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>();
filter.setName("orderFilter");
filter.setFilter(new OrderFilter());
filter.setOrder(-1);
return filter;
}
复制代码
此外格外注意, @WebFilter
声明的Filter,优先级为2147483647
(最低优先级)
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛