Spring-web源码解析之Filter-OncePerRequestFilter

转自:  http://blog.csdn.net/ktlifeng/article/details/50630934


基于4.1.7.RELEASE


我们先看一个filter-mapping的配置 

[html]  view plain  copy
  1. <filter-mapping>  
  2.    <filter-name>encodingFilter</filter-name>  
  3.    <url-pattern>/*</url-pattern>  
  4.    <dispatcher>REQUEST</dispatcher>  
  5.    <dispatcher>ASYNC</dispatcher>  
  6. </filter-mapping>  

这里指定了一个ASYNC的配置,表明过滤异步请求,这个ASYNC即是枚举类DispatcherType中的一个元素,在Servlet3.0中,如果一个请求是DispatcherType.ASYNC类型的,那么在一个单一请求的过程中,filter能够被多个线程调用,也就是意味着一个filter可能在一次请求中被多次执行,这显然是会有问题的,那么spring是怎么避免这个问题的呢?这就是今天要说的OncePerRequestFilter。

直接看doFilter方法

[java]  view plain  copy
  1. @Override  
  2. public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  3.       throws ServletException, IOException {  
  4.   
  5.    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  6.       throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  7.    }  
  8.    HttpServletRequest httpRequest = (HttpServletRequest) request;  
  9.    HttpServletResponse httpResponse = (HttpServletResponse) response;  
  10.   
  11.    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  12.    boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;  
  13.   
  14.    if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {  
  15.   
  16.       // Proceed without invoking this filter...  
  17.       filterChain.doFilter(request, response);  
  18.    }  
  19.    else {  
  20.       // Do invoke this filter...  
  21.       request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  22.       try {  
  23.          doFilterInternal(httpRequest, httpResponse, filterChain);  
  24.       }  
  25.       finally {  
  26.          // Remove the "already filtered" request attribute for this request.  
  27.          request.removeAttribute(alreadyFilteredAttributeName);  
  28.       }  
  29.    }  
  30. }  

扫描二维码关注公众号,回复: 3926177 查看本文章

从源码可以看出,spring会给已经过滤过的request设置一个attribute,在filter链和目标方法执行完毕之后才会释放这个attribute,attribute的名字是从  getAlreadyFilteredAttributeName() 方法得来,默认为filter的名字加后缀,如果filter没有完全初始化,则改为类名加后缀,后缀为“.FILTERED”

[java]  view plain  copy
  1. protected String getAlreadyFilteredAttributeName() {  
  2.    String name = getFilterName();  
  3.    if (name == null) {  
  4.       name = getClass().getName();  
  5.    }  
  6.    return name + ALREADY_FILTERED_SUFFIX;  
  7. }  

获取完名字之后,需要进行判断是否已经执行过这个filter了,判断条件有3个

1 是否有hasAlreadyFilteredAttribute 

2 是否skipDispatch

3 是否不进行过滤

我们直接看2和3,步骤3里,根据shouldNotFilter(httpRequest)来判断是否进行过滤,其实现依赖于子类。

在2里面判断条件有两种

[java]  view plain  copy
  1. private boolean skipDispatch(HttpServletRequest request) {  
  2.    if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {  
  3.       return true;  
  4.    }  
  5.    if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {  
  6.       return true;  
  7.    }  
  8.    return false;  
  9. }  

1 是异步并且不应该过滤异步,则skipDispatch为true,即不进行过滤

2 是ERROR请求并且不应该过滤ERROR,同样返回true

在上述所有判断条件完成之后,就可以决定是否执行

[java]  view plain  copy
  1. doFilterInternal(httpRequest, httpResponse, filterChain);  

方法了,这个方法就是子类具体实现的方法,其中之一便是前一篇文章中讲的CharacterEncodingFilter。

还需要注意的一个方法是 

[java]  view plain  copy
  1. protected boolean isAsyncStarted(HttpServletRequest request) {  
  2.    return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();  
  3. }  

如果这个返回true,那么当前线程结束时不会将response提交回去。

1

转自:  http://blog.csdn.net/ktlifeng/article/details/50630934


基于4.1.7.RELEASE


我们先看一个filter-mapping的配置 

[html]  view plain  copy
  1. <filter-mapping>  
  2.    <filter-name>encodingFilter</filter-name>  
  3.    <url-pattern>/*</url-pattern>  
  4.    <dispatcher>REQUEST</dispatcher>  
  5.    <dispatcher>ASYNC</dispatcher>  
  6. </filter-mapping>  

这里指定了一个ASYNC的配置,表明过滤异步请求,这个ASYNC即是枚举类DispatcherType中的一个元素,在Servlet3.0中,如果一个请求是DispatcherType.ASYNC类型的,那么在一个单一请求的过程中,filter能够被多个线程调用,也就是意味着一个filter可能在一次请求中被多次执行,这显然是会有问题的,那么spring是怎么避免这个问题的呢?这就是今天要说的OncePerRequestFilter。

直接看doFilter方法

[java]  view plain  copy
  1. @Override  
  2. public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  
  3.       throws ServletException, IOException {  
  4.   
  5.    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {  
  6.       throw new ServletException("OncePerRequestFilter just supports HTTP requests");  
  7.    }  
  8.    HttpServletRequest httpRequest = (HttpServletRequest) request;  
  9.    HttpServletResponse httpResponse = (HttpServletResponse) response;  
  10.   
  11.    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();  
  12.    boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;  
  13.   
  14.    if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {  
  15.   
  16.       // Proceed without invoking this filter...  
  17.       filterChain.doFilter(request, response);  
  18.    }  
  19.    else {  
  20.       // Do invoke this filter...  
  21.       request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);  
  22.       try {  
  23.          doFilterInternal(httpRequest, httpResponse, filterChain);  
  24.       }  
  25.       finally {  
  26.          // Remove the "already filtered" request attribute for this request.  
  27.          request.removeAttribute(alreadyFilteredAttributeName);  
  28.       }  
  29.    }  
  30. }  

从源码可以看出,spring会给已经过滤过的request设置一个attribute,在filter链和目标方法执行完毕之后才会释放这个attribute,attribute的名字是从  getAlreadyFilteredAttributeName() 方法得来,默认为filter的名字加后缀,如果filter没有完全初始化,则改为类名加后缀,后缀为“.FILTERED”

[java]  view plain  copy
  1. protected String getAlreadyFilteredAttributeName() {  
  2.    String name = getFilterName();  
  3.    if (name == null) {  
  4.       name = getClass().getName();  
  5.    }  
  6.    return name + ALREADY_FILTERED_SUFFIX;  
  7. }  

获取完名字之后,需要进行判断是否已经执行过这个filter了,判断条件有3个

1 是否有hasAlreadyFilteredAttribute 

2 是否skipDispatch

3 是否不进行过滤

我们直接看2和3,步骤3里,根据shouldNotFilter(httpRequest)来判断是否进行过滤,其实现依赖于子类。

在2里面判断条件有两种

[java]  view plain  copy
  1. private boolean skipDispatch(HttpServletRequest request) {  
  2.    if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {  
  3.       return true;  
  4.    }  
  5.    if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {  
  6.       return true;  
  7.    }  
  8.    return false;  
  9. }  

1 是异步并且不应该过滤异步,则skipDispatch为true,即不进行过滤

2 是ERROR请求并且不应该过滤ERROR,同样返回true

在上述所有判断条件完成之后,就可以决定是否执行

[java]  view plain  copy
  1. doFilterInternal(httpRequest, httpResponse, filterChain);  

方法了,这个方法就是子类具体实现的方法,其中之一便是前一篇文章中讲的CharacterEncodingFilter。

还需要注意的一个方法是 

[java]  view plain  copy
  1. protected boolean isAsyncStarted(HttpServletRequest request) {  
  2.    return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();  
  3. }  

如果这个返回true,那么当前线程结束时不会将response提交回去。

猜你喜欢

转载自blog.csdn.net/xinyuan_java/article/details/78341464