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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
简介: 基于4.1.7.RELEASE我们先看一个filter-mapping的配置  encodingFilter /* REQUEST ASYNC这里指定了...

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

目录
相关文章
|
15天前
|
存储 NoSQL Redis
redis 6源码解析之 object
redis 6源码解析之 object
43 6
|
4天前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
|
8天前
|
开发者 Python
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
32 1
|
8天前
|
开发者 Python
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
22 1
|
22天前
|
负载均衡 Java Spring
@EnableFeignClients注解源码解析
@EnableFeignClients注解源码解析
48 14
|
15天前
|
NoSQL Redis
redis 6源码解析之 ziplist
redis 6源码解析之 ziplist
16 5
|
4天前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
16天前
|
XML Java 数据库连接
深入解析 Spring 配置文件:从基础到高级
【8月更文挑战第3天】Spring配置文件是构建与管理Spring应用的核心,它涵盖了从基础到高级的各种配置技巧。基础配置采用`.xml`格式定义Bean及其依赖;中级配置包括设置Bean作用域及引入属性文件;高级配置则涉及AOP、事务管理和与其他框架的整合。熟练掌握这些配置能帮助开发者构建出更为灵活且易维护的应用系统。
|
2月前
|
XML Java 数据格式
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
67 3
|
22天前
|
负载均衡 Java API
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
38 11

推荐镜像

更多