源码解析Servlet Filter原理
零、前言
本文旨在从源码的角度解读过滤器的原理。
使用的源码为springboot2.2内置的tomcat9。
约定: 下面的所说的web服务器就是tomcat,war指的就是我们开发的web应用程序。
一、流程概述
为了明确filter所在位置,先说一个整体视角:以tomcat为例,所谓“web开发”,就是完善tomcat逻辑的过程。
解释:我们开发的war包是个完整的应用程序吗? 不是,没有tomcat这样的web服务器,war根本不能运行。war包只是web服务的一部分,是用来被tomcat“回调函数” 调用的一部分程序。
而filter就是在tomcat服务里的一部分(就是我们所说的tomcat源码的一部分)。
二、大致流程
流程分为两个阶段:
- tomcat启动阶段
这个阶段的目的是加载所有的filter信息(注意这里加载的是filter信息,而真正的filter是第二阶段加载的) - tomcat收到请求阶段
这一阶段会在每次收到请求的时候都执行一遍。
首先是根据第一阶段加载的信息,加载进所有的filter,形成一个FilterChain(一个由filter组成的链条,有点像一串鞭炮,后面会重点讲这个链条的运行原理)
然后执行(点燃鞭炮的导火索)。这个鞭炮执行完,再转发到用户真正请求的接口。
三、详细流程
无论是第一阶段,还是第二阶段,都在org.apache.catalina.core这个tomcat核心包里。
第一阶段
位置(类名):StandardContext
核心方法:filterStart()
最后将filter信息存储在filterConfigs这个Map中
第二阶段
位置(类名):StandardWrapperValve
核心方法:invoke()
这个方法比较长,这里只截取中间和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就被点燃了,实际上就是一种递归思想,如下图所示
这一串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的包里找到。
上一篇: 时尚品牌软文营销写作标题设置试试这几招
推荐阅读
-
Laravel框架源码解析之模型Model原理与用法解析
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
Spring MVC源码(三) ----- @RequestBody和@ResponseBody原理解析
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)
-
Mybaits 源码解析 (三)----- Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)
-
持久层Mybatis3底层源码分析,原理解析
-
荐 【dubbo源码解析】--- dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析
-
Andorid jar库源码Bolts原理解析
-
Laravel框架源码解析之入口文件原理分析