SpringBoot&Filter的详解
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理
大概流程图如下
应用场景
自动登录
统一设置编码格式
访问权限控制
敏感字符过滤等
......
1、Filter的使用
要想使用filter,需要写一个方法继承Filter类,我们写如下两个自己的Filter类,首先是FirstFilter类,其中@Order里边的数字越小代表越先被该Filter过滤,@WebFilter
代表这是个Filter类并把这个类注入到容器中。
注意:
SpringBoot 实现 Filter 两种方式:
自定义Filter通过FilterRegistrationBean 类来注入;(不推荐)
自定义Filter通过 实现接口 Filter方法,@WebFilter注入。
FirstFilter
@Order(1) @WebFilter(filterName="firstFilter", urlPatterns="/*") public class FirstFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("first filter 1111111111111111111"); chain.doFilter(request, response); System.out.println("first filter 2222222222222222222"); } @Override public void destroy() { } }
SecondFilter
@Order(2) @WebFilter(filterName="secondFilter", urlPatterns="/*") public class SecondFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("second filter 1============================="); System.out.println("before:" + response); chain.doFilter(request, response); System.out.println("after:" + response); System.out.println("second filter 2============================="); } @Override public void destroy() { } }
TestController
@RestController public class TestController { @GetMapping("/test1") public String test1() { System.out.println("method in controller"); return "test1"; } }
postman测试:
http://localhost:8080/test1
输出结果:
first filter 1111111111111111111 second filter 1============================= before:org.apache.catalina.connector.ResponseFacade@1d2aafd8 method in controller after:org.apache.catalina.connector.ResponseFacade@1d2aafd8 second filter 2============================= first filter 2222222222222222222
过程:
总结:
我们可以看出代码执行的流程,首先请求被firstfilter截获,打印出first filter 1,然后去执行chain.doFilter(request, response),这句话代表着请求会转发给过滤器链上下一个对象,也就是secondfilter,所以打印出secondfilter里的second filter 1,接下来再执行secondfilter里的chain.dofilter()方法,请求再转发给下一个对象,由于没有其他的filter了,所以会转发给controller,打印出了controller类中的method in controller,接下来再去内存栈里调用secondfilter的print("second filter 2"),然后再去内存栈里调用firstfilter的print("first filter 1")。所以如果在自己实现的Filter类的doFilter方法里不加chain.doFilter(req, rep)是万万不行的,那样会导致请求到了这个filter里就不再往下走了,永远进不了controller中。
我们也可以在print("before:" + response)和print("after:" + response)这两个地方打上断点,然后调试一下,你会发现在before那里的response里是什么都么有的,而在after那里的response里则是已经有了test1字符串,也就是说controller类test1方法的返回值已经添加进了response,所以如果你想对请求的response做一下过滤处理,那么一定要在chain.doFilter(res, rep)之后写你的逻辑。
2、@WebFilter注解说明
@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
@WebFilter 的常用属性
属性名 | 类型 | 说 明 |
---|---|---|
filterName |
String | 指定过滤器的 name 属性,等价与 <filter-name> 标签 |
value |
String[] | 该属性等价于 <url-pattern> 标签,但是两者不应该同时使用 |
urlPatterns |
String[] | 指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签。 |
servletNames |
String[] |
用于指定过滤的servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 <servlet-name> 的取值。 |
dispatcherTypes |
DispatcherType[] | 指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
initParams |
WebInitParam[] | 用于设置该过滤器类的一些初始化参数,等同于<init-param> 标签 |
asyncSupported |
Boolean | 声明过滤器是否支持异步操作模式,等价于 <async supported> 标签 |
description |
String | 对该过滤器的描述信息,等价于 <description> 标签 |
displayName |
String | 过滤器显示名,通常配合工具使用,等同与<display-name> 标签 |
3、责任链模式的实际应用
Filter和FilterChain都是怎么用责任链模式实现的,自定义实现一下, 使用Filter模式来模拟
Filter接口
public interface Filter { /** * 过滤器 * @param request 请求对象 * @param response 返回对象 */ public void doFilter(Request request, Response response, FilterChain chain); }
FilterChain类
{ 过滤器链 }
/** * @Desc: TODO 过滤器链【执行责任链模式的主要成员】 */ public class FilterChain implements Filter { /** * 过滤器列表 */ private List<Filter> filters = new ArrayList<>(); int index = 0; /** * 添加过滤器 * @param filter */ public FilterChain addFilter(Filter filter) { filters.add(filter); return this; } @Override public void doFilter(Request request, Response response, FilterChain chain) { if(index == filters.size()) { /** * 真正处理请求(此时开始处理 controller业务逻辑) */ return; } Filter filter = filters.get(index); index++; filter.doFilter(request, response, chain); } }
Request类和Response类
public class Request { public String requestStr; }
public class Response { public String responseStr; }
实现类(业务中需要的过滤器,以下举例)
EncodeFilter
{ 设置统一编码 }
public class EncodeFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { request.requestStr = request.requestStr + " 设置编码 UTF-8"; System.out.println("EncodeFilter request Str:" + request.requestStr); chain.doFilter(request, response, chain); response.responseStr = response.responseStr + "-------------设置编码 UTF-8"; System.out.println("EncodeFilter response Str:" + response.responseStr); } }
VerifyParameterFilter
{ 参数校验 }
public class VerifyParameterFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { request.requestStr = request.requestStr + " 参数验证 成功"; System.out.println("VerifyParameterFilter request str:" + request.requestStr); chain.doFilter(request, response, chain); response.responseStr = response.responseStr + "======================"; System.out.println("VerifyParameterFilter response str:" + response.responseStr); } }
测试
public class FilterTest { public static void main(String[] args) { String msg = "张三"; Request request = new Request(); request.requestStr = msg; Response response = new Response(); response.responseStr = "success *****"; FilterChain fc = new FilterChain(); fc.addFilter(new EncodeFilter()) .addFilter(new VerifyParameterFilter()) .doFilter(request, response, fc); } }
结果:
EncodeFilter request Str:张三 设置编码 UTF-8 VerifyParameterFilter request str:张三 设置编码 UTF-8 参数验证 成功 VerifyParameterFilter response str:success *****====================== EncodeFilter response Str:success *****======================-------------设置编码 UTF-8
总结
Filter是针对请求进行拦截、意在请求前进行一些过滤、权限校验、日志记录、统一编码等
所有Filter统一在FilterChain中组成一个链条,然后调用也统一由FilterChain来协调,确保以链条的模式执行
责任链模式是一种重要的设计模式,如Servlet中的Filter模式、Mybatis中的Plugin模式等都是责任链模式的体现