基于4.1.7.RELEASE
我们先看一个filter-mapping的配置
<filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ASYNC</dispatcher> </filter-mapping>
这里指定了一个ASYNC的配置,表明过滤异步请求,这个ASYNC即是枚举类DispatcherType中的一个元素,在Servlet3.0中,如果一个请求是DispatcherType.ASYNC类型的,那么在一个单一请求的过程中,filter能够被多个线程调用,也就是意味着一个filter可能在一次请求中被多次执行,这显然是会有问题的,那么spring是怎么避免这个问题的呢?这就是今天要说的OncePerRequestFilter。
直接看doFilter方法
@Override public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { throw new ServletException("OncePerRequestFilter just supports HTTP requests"); } HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) { // Proceed without invoking this filter... filterChain.doFilter(request, response); } else { // Do invoke this filter... request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { doFilterInternal(httpRequest, httpResponse, filterChain); } finally { // Remove the "already filtered" request attribute for this request. request.removeAttribute(alreadyFilteredAttributeName); } } }
从源码可以看出,spring会给已经过滤过的request设置一个attribute,在filter链和目标方法执行完毕之后才会释放这个attribute,attribute的名字是从 getAlreadyFilteredAttributeName() 方法得来,默认为filter的名字加后缀,如果filter没有完全初始化,则改为类名加后缀,后缀为“.FILTERED”
protected String getAlreadyFilteredAttributeName() { String name = getFilterName(); if (name == null) { name = getClass().getName(); } return name + ALREADY_FILTERED_SUFFIX; }
获取完名字之后,需要进行判断是否已经执行过这个filter了,判断条件有3个
1 是否有hasAlreadyFilteredAttribute
2 是否skipDispatch
3 是否不进行过滤
我们直接看2和3,步骤3里,根据shouldNotFilter(httpRequest)来判断是否进行过滤,其实现依赖于子类。
在2里面判断条件有两种
private boolean skipDispatch(HttpServletRequest request) { if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) { return true; } if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) { return true; } return false; }
1 是异步并且不应该过滤异步,则skipDispatch为true,即不进行过滤
2 是ERROR请求并且不应该过滤ERROR,同样返回true
在上述所有判断条件完成之后,就可以决定是否执行
doFilterInternal(httpRequest, httpResponse, filterChain);
方法了,这个方法就是子类具体实现的方法,其中之一便是前一篇文章中讲的CharacterEncodingFilter。
还需要注意的一个方法是
protected boolean isAsyncStarted(HttpServletRequest request) { return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted(); }
如果这个返回true,那么当前线程结束时不会将response提交回去。