欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

源码解析Servlet Filter原理

程序员文章站 2022-06-08 13:21:28
...

零、前言

本文旨在从源码的角度解读过滤器的原理。
使用的源码为springboot2.2内置的tomcat9。
约定: 下面的所说的web服务器就是tomcat,war指的就是我们开发的web应用程序。

一、流程概述

为了明确filter所在位置,先说一个整体视角:以tomcat为例,所谓“web开发”,就是完善tomcat逻辑的过程。
解释:我们开发的war包是个完整的应用程序吗? 不是,没有tomcat这样的web服务器,war根本不能运行。war包只是web服务的一部分,是用来被tomcat“回调函数” 调用的一部分程序。
而filter就是在tomcat服务里的一部分(就是我们所说的tomcat源码的一部分)。

二、大致流程

源码解析Servlet Filter原理

流程分为两个阶段:

  1. tomcat启动阶段
    这个阶段的目的是加载所有的filter信息(注意这里加载的是filter信息,而真正的filter是第二阶段加载的)
  2. tomcat收到请求阶段
    这一阶段会在每次收到请求的时候都执行一遍。
    首先是根据第一阶段加载的信息,加载进所有的filter,形成一个FilterChain(一个由filter组成的链条,有点像一串鞭炮,后面会重点讲这个链条的运行原理)
    然后执行(点燃鞭炮的导火索)。这个鞭炮执行完,再转发到用户真正请求的接口。

三、详细流程

无论是第一阶段,还是第二阶段,都在org.apache.catalina.core这个tomcat核心包里。
源码解析Servlet Filter原理

第一阶段

位置(类名):StandardContext
核心方法:filterStart()
源码解析Servlet Filter原理
最后将filter信息存储在filterConfigs这个Map中
源码解析Servlet Filter原理

第二阶段

位置(类名):StandardWrapperValve
核心方法:invoke()
这个方法比较长,这里只截取中间和filter相关的一些内容,从前面的流程图也能看出一共分为两步。
源码解析Servlet Filter原理

第一步:加载filter

上面的注释也解释了:Create the filter chain for this request
就是ApplicationFilterFactory这个工厂类,根据filter信息,生成一个一个filter,然后执行filterChain.addFilter(filterConfig),形成一个filterChain
【不应该是addFilter吗,为什么这里add的是filterConfig? 这里可以把filterConfig认为就是filter,通过源码可以看到filterConfig封装了filter信息】

第二步:点燃filterChain“鞭炮链”

通过执行filterChain.doFilter(request.getRequest(), response.getResponse());
通过跟踪代码,最后执行到filter.doFilter(request, response, this);
这一串filter就被点燃了,实际上就是一种递归思想,如下图所示
源码解析Servlet Filter原理

这一串filter中就包括我们在web项目中自己写的filter(之所以说“包括”,是因为里面还会由框架自带的一些filter,比如spring自带的一些filter)
假如下一个filter就是我们自己写的filter,那么程序就会执行到如下熟悉的代码中

/**
 * FilterChain的作用就是实现一个类似递归的“自驱动”链条
 * @author yuancan
 *
 */
@WebFilter("/*")
public class MyFilterImpl implements Filter {

	@Override
	public void doFilter(Request req, Response res, FilterChain chain) {
		//1. 处理request 
		
		//2. 执行filter链条的下一个filter
		chain.doFilter(req, res);//处理其他的request
		
		//3. 执行controller接口(这只是表示一下步骤,controller接口实际写在controller类里)
		
	}

}

在上面这个常见的filter中,有两个步骤(第三个步骤是为了清楚的表示filter和我们写的接口的顺序关系,实际接口并没有写在这里)
第一个步骤是处理request(你也可以不处理,但如果不处理request,那么这个filter实际上就没有存在的意义),一般我们都会对请求进行权限校验,如果不符合,就直接return;那么这个请求就到此结束了。
通过上面的图,我们可以知道,这个FilterChain的自驱动,实际是要和filter的写法相互配合完成一个递归的过程。
当所有的filter递归执行完之后,才会执行本次请求的接口。

四、其他

关于filter执行顺序

按滤器的文件名首字母顺序执行。
@Order无法对filter进行排序
因为@Order是spring的bean执行优先级的顺序,而filter并不属于spring的bean。

小插曲

本文的核心是javax.servlet.Filter这个接口,从包名上可以看出是java平台的,但从源码包里可以发现:javax.servlet并没有在jdk的rt包里, 而是在tomcat的包里。
javax.servlet为什么是在tomcat的包里,而不是在jdk的rt包里?
解释javax.servlet是java制定的标准,由具体的web服务器厂商去实现。类似的还有javax.persistence(java的jpa标准)可以在hibernate的包里找到。