Spring-web源码解析之Filter-OncePerRequestFilter:

基于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提交回去。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
org.springframework.security.authentication.InternalAuthenticationServiceException: null at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:123) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:95) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.9.RELEASE.jar:5.2.9.
07-20

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值