源码解析Servlet Filter原理

零、前言

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

一、流程概述

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

二、大致流程

image

流程分为两个阶段:

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

三、详细流程

无论是第一阶段,还是第二阶段,都在org.apache.catalina.core这个tomcat核心包里。
image

第一阶段

启动tomcat
位置(类名):StandardContext
核心方法:filterStart()
image
最后将filter信息存储在filterConfigs这个Map中
image

第二阶段

tomcat收到HTTP请求
位置(类名):StandardWrapperValve
核心方法:invoke()
这个方法比较长,这里只截取中间和filter相关的一些内容,从前面的流程图也能看出一共分为两步。
image

第一步:加载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);

相关核心方法:org.apache.catalina.core.ApplicationFilterChain类的internalDoFilter方法,如下面截图所示:
image
image

这一串filter就被点燃了,实际上就是一种递归思想,如下图所示
image

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

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

	@Override
	public void doFilter(Request req, Response res, FilterChain chain) {
		//1. 处理request 
		
		//2. 执行chain的doFilter(形成递归“回环”,从而执行完所有的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的包里找到。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值