Filter基本上能够说存在全部的JavaWeb项目中,好比最基本的一个请求参数的编码
CharacterEncodingFilter
,你们通常都会配置下,那么filter是干吗的呢?java
本篇将主要集中在fitler的如下几个知识点:web
Filter称之为过滤器,是用来作一些拦截的任务, 在Servlet接受请求以前,作一些事情,若是不知足限定,能够拒绝进入Servletspring
从上面的图,能够看出一个Filter的工做流程:安全
一个http请求过来以后mvc
经过上面的流程,能够推算使用场景:app
要使用一个Filter,一半须要两步,实现Filter接口的自定义类,web.xml中对filter的定义curl
public interface Filter { public void init(FilterConfig filterConfig) throws ServletException; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy(); }
主要就三个方法,从命名来看,async
init
方法destroy
方法doFilter
,也就是主要的业务逻辑所在了详细case后面再说ide
接下来就是xml的配置了,和Servlet相似,每自定义一个,都须要在xml中加上一个配置(挺繁琐的操做的)学习
<!-- 解决乱码的问题 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <async-supported>true</async-supported> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
配置也比较简单了,一个 <filter> 一个 <filter-mapping> 前者定义具体的Filter,后者表示这个Filter拦截的URL (看起来和Servlet的配置规则没什么两样)
咱们的实例,就拿大名鼎鼎的CharacterEncodingFilter
来讲明,顺带膜拜下Spring的大神的优秀源码
public class CharacterEncodingFilter extends OncePerRequestFilter { private String encoding; private boolean forceEncoding = false; public CharacterEncodingFilter() { } public CharacterEncodingFilter(String encoding) { this(encoding, false); } public CharacterEncodingFilter(String encoding, boolean forceEncoding) { Assert.hasLength(encoding, "Encoding must not be empty"); this.encoding = encoding; this.forceEncoding = forceEncoding; } public void setEncoding(String encoding) { this.encoding = encoding; } public void setForceEncoding(boolean forceEncoding) { this.forceEncoding = forceEncoding; } @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) { request.setCharacterEncoding(this.encoding); if (this.forceEncoding) { response.setCharacterEncoding(this.encoding); } } filterChain.doFilter(request, response); System.out.printl("servelt 执行完成,又返回filter"); } }
上面的实现比较简单,主要将视线集中在 doFilterInternal
方法内部,若是要设置编码参数,则直接修改 HttpServletRequest
, HttpServletResponse
两个参数,操做完成以后,执行下面这一行
filterChain.doFilter(request, response);
注意
因此,若是你不但愿继续往下走,那么就简单了,不执行上面的那一行便可
问题一:看了上面的源码,一个很明显的问题就是,参数怎么设置的?
仔细看上面的源码,发现自定义Filter是继承 org.springframework.web.filter.OncePerRequestFilter
而不是直接实现的 Filter 接口,并且方法内也没有显示的实现 init()
方法,全部很容易猜到是父类中实现了参数的初始化过程
具体的实现逻辑是在 org.springframework.web.filter.GenericFilterBean#init
中,一样是Spring实现的,主要代码捞出来
public final void init(FilterConfig filterConfig) throws ServletException { Assert.notNull(filterConfig, "FilterConfig must not be null"); if (logger.isDebugEnabled()) { logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'"); } this.filterConfig = filterConfig; // Set bean properties from init parameters. try { PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { String msg = "Failed to set bean properties on filter '" + filterConfig.getFilterName() + "': " + ex.getMessage(); logger.error(msg, ex); throw new NestedServletException(msg, ex); } // Let subclasses do whatever initialization they like. initFilterBean(); if (logger.isDebugEnabled()) { logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully"); } }
看上面一大串的代码,到底干了嘛? 简单来说,就是获取xml中配置的参数,而后填充到Filter对象中(对Srping而言,CharacterEncodingFilter就是一个bean),这个具体的逻辑和本篇关系不大,就直接跳过了
问题二:在Filter层中能够获取参数么
从doFilter的方法签名中看,既然有Request参数,那么应该是能够获取到请求参数的,那么实际验证一下
先实现一个最最最简单的Filter
public class TestFilter 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("in filter"); System.out.println("args: " + JSON.toJSONString(request.getParameterMap())); chain.doFilter(request, response); System.out.println("out filter"); } @Override public void destroy() { } }
开始测试
curl -d 'name=Hello&password=world' http://127.0.0.1:8088/123
输出以下
in filter args: {"name":["Hello"],"password":["world"]} out filter
注意
在Filter中获取参数时,最好不要直接使用获取请求流的方式,若是获取请求流,那么Servlet就获取不到请求参数了
问题三:多个filter的顺序怎么定
前面学习Servlet的时候,也有这个问题,一个URL被多个Servlet命中了,那么前后顺序是怎样的呢?
那么Filter呢,他们的区别仍是比较明显的,不少Filter都是拦截全部的请求,即不少Filter的命中规则都是同样的,那么怎么办?
测试case以下,咱们定义三个Filter:
// ATestFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in ATestFilter"); chain.doFilter(request, response); } // TestFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in TestFilter"); chain.doFilter(request, response); } // ServletFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in ServletFilter"); chain.doFilter(request, response); }
对应的xml配置以下
<filter> <filter-name>servletFilter</filter-name> <filter-class>com.test.ServletFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>servletFilter</filter-name> <servlet-name>mvc-dispatcher</servlet-name> </filter-mapping> <filter> <filter-name>testFilter</filter-name> <filter-class>com.test.TestFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>testFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>atestFilter</filter-name> <filter-class>com.test.ATestFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>atestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
输出结果
in TestFilter in ATestFilter in ServletFilter
Filter 一般用于JavaWeb的过滤使用,经过doFilter方法中执行 chain.doFilter(request, response);
,进入下一个Filter或者Servlet执行逻辑,当执行完成以后,依然会回到Filter这一层,继续走下去
针对上面的逻辑,Filter的常见应用场景有:
Filter的执行顺序:
Filter的注意事项:
chain.doFilter(request, response)
, 最后把它放在finnal块中,防止你在Filter中的代码抛异常致使进入不到后续的逻辑尽信书则不如,已上内容,纯属一家之言,因本人能力通常,看法不全,若有问题,欢迎批评指正