首页 课程 师资 教程 报名

Java过滤器链的原理分析

  • 2022-08-10 12:36:09
  • 1062次 动力节点

在很多Java Web项目中,我们会在web.xml中配置一些过滤器来拦截请求,比如下面的编码过滤器来解决乱码:

<过滤器>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<初始化参数>
		<param-name>编码</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
	<初始化参数>
		<param-name>forceEncoding</param-name>
		<param-value>真</param-value>
	</init-param>
	</过滤器>
<过滤器映射>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

谁调用了这些过滤器?谁组织了他们并确保了他们的执行顺序?过滤器演示如下:

@WebFilter(filterName="FilterDemo", urlPatterns={"/**"})
@Order(1)//当有多个过滤器时,指定过滤器的顺序
公共类 SecurityFilter 实现 Filter{
	private static final Logger LOGGER = LoggerFactory.getLogger(FilterDemo.class);	
	@Override
	公共无效销毁(){		
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			抛出 IOException,ServletException {
		//TODO 做一些过滤动作;
		链.doFilter(请求,响应);
	}
	@Override
	公共无效初始化(FilterConfig arg0)抛出 ServletException {
		//TODO 自动生成的方法存根	}
}

本以为这样写后过滤器会起作用,但是请求进来后,发现过滤器没有执行。后来想到 Spring 的工作是由 beanFactry 中注册的每个 bean 完成的,所以可能需要在 beanFactory 中注册这个过滤器,像这样:

@Configuration//表示这是一个Spring配置文件
@EnableSwagger2//swagger是一个restful接口文档在线自动生成+功能测试功能框架
@RefreshScope//允许依赖配置的bean在Spring cloud config配置文件更改后无需重启服务即可自动更新
公共类 GlobalBeanConfig {
   //将过滤器添加到Spring配置中,否则只会写过滤器类,不添加配置不起作用
    @Bean//使用代码配置xml形式的<bean></bean>
    公共过滤器 filterDemo(){
        返回新的 FilterDemo();
    }
    @Bean(name = "loggerInteceptor")
    公共 GlobalAspectInteceptor getLoggerInteceptor() {
        返回新的 GlobalAspectInteceptor();
    }
    @豆
    公共 ThreadPoolTask​​Executor globalTask​​Executor(
            @Value("${global.thread.pool.corePoolSize}") 整数 corePoolSize,
            @Value("${global.thread.pool.maxPoolSize}") 整数 maxPoolSize,
            @Value("${global.thread.pool.queueCapacity}") 整数 queueCapacity
    ) {
        ThreadPoolTask​​Executor 执行器 = new ThreadPoolTask​​Executor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadGroupName("globalTask​​Executor");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();
        返回执行者;
    }
}

添加评论后,它立即生效。既然知道了工具的使用方法,那我们就来拆解一下工具,看看效果如何。通过断点调试发现过滤器中的两个关键类在起作用:ApplicationFilterConfig和ApplicationFilterChain。

这两个类在 org.apache.catalina.core 包下。可以肯定的是,tomcat 容器管理过滤器链。

接下来,我们看一下ApplicationFilterConfig,先上部分源码:

Final 类 public ApplicationFilterConfig实现了 FilterConfig , Serializable {
      Private Long Final static serialVersionUID = 1L ;
 静态最终StringManager SM = StringManager 。getManager(“org.apache.catalina.core”);
 私有静态最终Log log = LogFactory 。getLog (
  ApplicationFilterConfig.class ) ; 私有静态最终列表<字符串>空字符串=收藏。空列表();
 私有最终瞬态上下文上下文;
 私有瞬态过滤器 filter = null ;
 私人最终过滤器过滤器过滤器;
 私有瞬态InstanceManager instanceManager ;
 私有对象名oname ;
ApplicationFilterConfig(Context context , FilterDef filterDef) throws ClassCastException , ClassNotFoundException , IllegalAccessException , InstantiationException ,ServletException 、 InvocationTargetException 、 NamingException 、 IllegalArgumentException 、 NoSuchMethodException 、 SecurityException {
          this . 上下文=上下文;
 这个。过滤器定义=过滤器定义;
 IF (filterDef . getFilter () == null ) {
              this . getFilter () ;
}其他{
             这个.过滤器=过滤器定义。getFilter () ;
这个。获取实例管理器()。新实例(这个。过滤器);
 这个。初始化过滤器();
}
    }
}

通过分析可以发现,这个类是用来保存一个过滤器的。构造方法传入的上下文必须是tomcat配置文件下context.xml配置的应用环境。FliterDef是过滤器的描述信息,应该通过在web.xml(或其他地方)中配置过滤器参数来构造。

好了,重点来了——ApplicationFilterChain ,名字就透露了它的作用,就是它组织了分散的过滤器。结合上面我的猜测,它应该有一个数组或列表来保存ApplicationFilterConfig,以及一个过滤器光标来记录当前过滤器去了哪里。源代码(部分)如下:

public final 类ApplicationFilterChain实现FilterChain {
      private static final ThreadLocal < ServletRequest > lastServicedRequest ;
 private static final ThreadLocal < ServletResponse > lastServicedResponse ;
 公共静态最终 int INCREMENT = 10 ;
 私有ApplicationFilterConfig[] 过滤器=新ApplicationFilterConfig[ 0 ] ;
 私人 int pos = 0 ;
 私人 int n = 0 ;
 私有Servlet servlet = null ;
 私有布尔servletSupportsAsync = false ;
 私有静态最终StringManager sm ;
 private static final Class <?> [] classType ;
 私有静态最终类<?> [] classTypeUsedInService ;
 公共应用程序过滤器链(){
    }
    public void doFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException {
          if (Globals . IS_SECURITY_ENABLED) {
              final ServletRequest req = request ;
 最终的 ServletResponse res = response ;
 尝试{ 
                AccessController 。DoPrivileged( new PrivilegedExceptionAction() {
                      public Void run() throws ServletException ,IO异常{
                        应用过滤链。这. InternalDoFilter(req , res) ;
 返回空值;
}                 }) ;
}捕捉(PrivilegedActionException var7){
                异常 e = var7 。获取异常 () ;
 if (e instanceof ServletException) {
                      throw (ServletException)e ;
}
                if (e instanceof IOException) {
                      throw (IOException)e ;
}
                if (e instanceof RuntimeException) {
                      throw (RuntimeException)e ;
}
                throw new ServletException(e .getMessage () , e) ;
}
        }其他{
             这个. internalDoFilter(请求,响应);
}
    }
    private void internalDoFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException
          { if ( this.pos < this.n ) {
            ApplicationFilterConfig e1 =这个。过滤器[这个。位置++ ] ;
试试{
                过滤器 res1 = e1 。获取过滤器();
 if (request . IsAsyncSupported() && "false" . EqualsIgnoreCase(e1 . GetFilterDef () . GetAsyncSupported ())) {
                    请求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布尔值。假);
}
                如果(全局。IS_SECURITY_ENABLED){
                    主体 principal1 = ((HttpServletRequest)request) 。获取用户主体();
Object[] args1 = new Object[]{request , response , this } ;
安全实用程序。DoAsPrivilege( "doFilter" , res1 , classType , args1 , principal1) ;
}其他{
                    水库1 。doFilter(request , response , this ) ;
}
            } catch (ServletException | RuntimeException | IOException var15) {
                  throw var15 ;
}捕捉(可抛出的 var16){
                可抛出的res = ExceptionUtils 。UnwrapInvocationTargetException(var16) ;
异常实用程序。HandleThrowable(res) ;
 throw new ServletException( sm.GetString ( " filterChain.filter" ) , res) ;
        } else {
             尝试{
                  if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
                    最后服务请求。设置(请求);
最后服务响应。设置(响应);
}
                if (request .isAsyncSupported () &&! this .servletSupportsAsync ) {
                    请求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布尔值。假);
}
                如果( HttpServletRequest的请求实例&& HttpServletResponse的响应实例&&全局。IS_SECURITY_ENABLED){
                    主体主体= ((HttpServletRequest)request) 。获取用户主体();
Object[] args = new Object[]{request , response} ;
安全实用程序。DoAsPrivilege( "service "
 , this.Servlet , classTypeUsedInService , args , principal ) ; }其他{
                     这个. 小服务程序。服务(请求,响应);
}
            } catch (ServletException | RuntimeException | IOException var17) {
                  throw var17 ;
}捕捉(Throwable var18){
                可抛出的e = ExceptionUtils 。UnwrapInvocationTargetException(var18) ;
异常实用程序。HandleThrowable(e) ;
 throw new ServletException( sm.GetString ( " filterChain.servlet" ) , e) ;
}最后{
                  if (ApplicationDispatcher . WRAP_SAME_OBJECT) { 
                    lastServicedRequest 。设置((对象)空);
最后服务响应。设置((对象)空) ;
}
            }
        }
    }
    …… }

果然字段中有一个ApplicationFilterConfig[]用来存放一系列filter,pos用来存放当前filter位置,还有其他字段就不深入了。有兴趣的朋友可以自行探索。

我们来看两个关键方法:doFilter、internalDoFilter

doFilter的最终目的只有一个,调用internalDoFilter,中间可能会加一些安全策略,估计Globals.IS_SECURITY_ENABLE和是否开启https服务有关,具体没有仔细研究。

internalDoFilter的最终目的只有一个,就是调整当前pos指向的filter链中某个filter的doFilter(request, response, this)方法。中间可能会添加一些安全策略,当所有过滤器都被调用时,进行一些收尾工作,包括调用servlet.service(request, response)方法处理真正的请求,以及清除当前存储的请求和响应threadLocal 为下一个请求做准备。

再梳理一下流程:

一个请求进来,先给自己给filterChain;

filterChain 启动过滤器链,从头开始,将请求交给第一个过滤器,并将自身传递给过滤器;

Filter在doFilter中完成自己的过滤逻辑,然后调用filterChain的doFilter开始下一个过滤器;

filterChain 光标移动,启动下一个过滤器,依此类推...

过滤器光标走到链尾,filterChain进行收尾工作;

最后,给出一个简单的流程图:

选你想看

你适合学Java吗?4大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交