SpringBoot + TomcatEmbeddedContext + Servlet + ApplicationFilterChain + Filter
背景: 在之前博客中有说明SpringBoot内嵌Web容器后,Filter及Servlet解析与注册流程的变化。将Filter实例封装成FilterRegistrationBean实例并添加到ServletContext后,到实际使用Filter完成过滤功能之前,其实中间还有一些管理流程。本文将在此背景下,继续追加描述此块逻辑。
博客内容精选:
1、Servlet请求体重复读&修改新姿势
2、根据请求获取后端接口详情
3、SpringBoot下Filter自动适配
4、Filter链式执行设计解读
一、首
先Filter相关定义经解析过存储在TomcatEmbeddedContext对象中,有三个重要变量:
1、private Map<String, FilterDef> filterDefs // 存储Filter定义元数据,如filterClass、name等
2、private final StandardContext.ContextFilterMaps filterMaps // FilterMap-匹配元数据,如String[] urlPatterns
3、private Map<String, ApplicationFilterConfig> filterConfigs // 由filterDefs可封装成ApplicationFilterConfig,核心为Filter实例
ApplicationFilterConfig(Context context, FilterDef filterDef) throws Exception{
this.context = context;
this.filterDef = filterDef;
if (filterDef.getFilter() == null) {
this.getFilter();
} else {
this.filter = filterDef.getFilter();
context.getInstanceManager().newInstance(this.filter);
this.initFilter();
}
}
我们都知道Filter要发布到Servlet容器,先以FilterRegistrationBean的形式封装,因注册Bean底层接口包含ServletContextInitializer,其在onStartup(ServletContext servletContext)方法执行下注册到ServletContext中去,以下为注册代码:
// 执行入口
public final void onStartup(ServletContext servletContext) throws ServletException {
..........省略.............
this.register(description, servletContext);
}
// 注册入口
protected final void register(String description, ServletContext servletContext) {
// 将Filter实例加入Context
D registration = this.addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
} else {
// 配置Filter匹配元数据
this.configure(registration);
}
}
// Filter实例对象注册
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = this.getFilter();
// 此处可将Filter实例添加到FilterDef =》filterDef.setFilter(filter)
// 返回ApplicationFilterRegistration(filterDef, this.context)对象
return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}
// URL等匹配元数据注册
protected void configure(Dynamic registration) {
super.configure(registration);
EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
..........省略..........
// 重点
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, StringUtils.toStringArray(this.urlPatterns));
}
public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... urlPatterns) {
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(this.filterDef.getFilterName());
..........
if (urlPatterns != null) {
for(int var7 = 0; var7 < urlPatterns.length; ++var7) {
String urlPattern = urlPatterns[var7];
filterMap.addURLPattern(urlPattern);
}
if (isMatchAfter) {
// 此处即为添加匹配元数据到Servlet上下文中
this.context.addFilterMap(filterMap);
} else {
this.context.addFilterMapBefore(filterMap);
}
}
}
二、ApplicationFilterFactory使用createFilterChain工厂方法创建ApplicationFilterChain链
createFilterChain有两大核心逻辑
1)创建ApplicationFilterChain实例
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request)request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain)req.getFilterChain();
if (filterChain == null) {
// 核心逻辑,直接创建Chain实例,且构造器无其他初始化逻辑
filterChain = new ApplicationFilterChain();
// chain对象复用,注意这里req对象是org.apache.catalina.connector.Request
// chain内容不复用,filterChain使用完毕会调用release方法释放资源
req.setFilterChain(filterChain);
}
}
} else {
filterChain = new ApplicationFilterChain();
}
2)过滤器链初始化【极简版】
// Servlet设置
filterChain.setServlet(servlet); // DispatcherServlet实例
StandardContext context = (StandardContext)wrapper.getParent(); // TomcatEmbeddedContext
// 获取Servlet上下文中的过滤器匹配元数据
FilterMap[] filterMaps = context.findFilterMaps();
if (filterMaps != null && filterMaps.length != 0) {
// 分发类型,一般为Request
DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
// 请求URI解析,此处忽略
String requestPath = xxx;
String servletName = wrapper.getName(); // 值为dispatcherServlet
FilterMap filterMap;
ApplicationFilterConfig filterConfig;
for(int index = 0; index < filterMaps.length; ++index) {
filterMap = filterMaps[index];
// 目前Springboot项目基本只需解析matchFiltersURL方法,根据URL匹配对应Filter
if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null) {
// 过滤器链中数组对象存的是ApplicationFilterConfig实例,而不是Filter实例,因为部分场景需要元数据作为判断条件
filterChain.addFilter(filterConfig);
}
}
}
// Servlet匹配Filter当前无用,仅作参考。结构类似,代码忽略
..............................
return filterChain;
}
三、过滤器链执行入口:StandardWrapperValve
在StandardWrapperValve中,由invoke(Request request, Response response)方法内执行过滤器链,StandardWrapperValve前也是通过责任链模式一步步传递过来,也有相似的Valve实例。
过滤器链执行流程很简单,可以简化为以下过程:
// 注:这里传给Filter的其实是RequestFacade对象,包装了底层的Request对象,Response同理
1、filterChain.doFilter(request.getRequest(), response.getResponse())
//过滤器链每次执行后,释放资源
3、filterChain.release()
总结:本文将Filter在Servlet上下文的注册流程、FilterChain过滤器链的封装流程基本讲述完成,其他Filter相关知识可参考之前其他博客内容,剩余细节不在展开。
思考:
1、过滤器池化相关概念,实际不是FilterChain的池化,其每次会recycle,而是org.apache.coyote.Request请求对象的池化(注意:此对象不是实现HttpServletRequest的org.apache.catalina.connector.Request)。Request对象较为底层且复杂,在并发量较低时,同一接口间隔请求都是一个Request对象,使用完成也会调用recycle回收,response同理。
2、FilterChain在Request池化基础上,每次不会新建,但是每次请求都会重新生成ApplicationFilterConfig数组。按现在Springboot请求流程,暂不清楚为啥需要recycle,(只是元数据封装,性能影响很小)可能考虑到Filter可能在服务期间被destroy?
3、至于线程安全问题,FilterChain是多例的,Filter是单例的,但是被多个FilterChain封装,也能并发执行,所以不要在Filter中定义共享变量。