【记录版】SpringBoot下Filter注册源码解读

本文详细讲述了SpringBoot中Filter的注册流程,包括FilterRegistrationBean的使用,以及Filter在Servlet容器中的应用、匹配和执行过程,重点介绍了ApplicationFilterChain的创建和初始化。

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())

2、中间过滤器链执行流程,已在其他博客说明

//过滤器链每次执行后,释放资源
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中定义共享变量。

<think>我们被要求品读Spring Boot自动配置的源码,重点在于理解其核心机制。根据之前的讨论,我们知道`AutoConfigurationImportSelector`是自动配置的核心类,但自动配置的源码阅读涉及多个方面。以下是一个系统的源码解读框架: ### Spring Boot自动配置源码核心解读 #### 1. **启动入口:`@SpringBootApplication`** ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration // 开启自动配置 @ComponentScan public @interface SpringBootApplication { // ... } ``` - `@EnableAutoConfiguration`是关键,它通过`@Import`导入了`AutoConfigurationImportSelector`。 #### 2. **核心选择器:`AutoConfigurationImportSelector`** - **继承结构**:实现`DeferredImportSelector`接口,因此其导入的配置类会延迟处理(在所有`@Configuration`类处理之后)。 - **核心方法**: ```java @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 1. 检查是否开启自动配置(默认开启) if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 2. 获取所有自动配置类(带过滤条件) AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } ``` - `getAutoConfigurationEntry()`方法负责加载和过滤配置类。 #### 3. **配置加载:`getAutoConfigurationEntry()`** ```java protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { // 获取@EnableAutoConfiguration的属性(如exclude) AnnotationAttributes attributes = getAttributes(annotationMetadata); // 加载候选配置类(从spring.factories) List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去重(避免重复配置) configurations = removeDuplicates(configurations); // 获取需要排除的配置类(通过exclude属性) Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 检查排除的类是否合法(必须是自动配置类) checkExcludedClasses(configurations, exclusions); // 移除排除的类 configurations.removeAll(exclusions); // 应用自动配置过滤器(条件注解过滤) configurations = getConfigurationClassFilter().filter(configurations); // 触发自动配置导入事件 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } ``` #### 4. **条件过滤:`AutoConfigurationImportFilter`** - 通过`spring.factories`中配置的`AutoConfigurationImportFilter`实现类进行过滤。 - 默认使用三个过滤器(位于`spring-boot-autoconfigure`包): 1. `OnClassCondition`:检查类路径是否存在指定类 2. `OnBeanCondition`:检查容器中是否存在指定Bean 3. `OnWebApplicationCondition`:检查是否Web应用 #### 5. **配置类解析:`AutoConfigurationGroup`** - 在`process()`方法中处理配置类,包括排序和分组。 #### 6. **自动配置类示例:`ServletWebServerFactoryAutoConfiguration`** ```java @Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { // ... } ``` - 条件注解控制:只有存在`ServletRequest`且是Servlet Web应用时生效。 - 通过`@Import`导入多个具体配置(Tomcat/Jetty/Undertow)。 ### 关键设计思想 1. **约定优于配置**:通过条件注解按需加载。 2. **扩展性**:通过`spring.factories`机制允许第三方starter贡献配置。 3. **性能优化**:延迟加载、条件过滤减少不必要的配置解析。 §§相关问题§§ 1. Spring Boot 如何确保自动配置类在常规配置类之后加载? 2. 在自动配置过程中,条件注解(如@ConditionalOnClass)的解析发生在哪个阶段? 3. 如何自定义一个 AutoConfigurationImportFilter 实现自定义过滤逻辑? 4. 自动配置类中使用@ConditionalOnMissingBean 注解的作用是什么?请举例说明。 5. Spring Boot 自动配置的源码中,哪些地方使用了设计模式?</think>### Spring Boot 自动配置源码深度解析 #### 一、核心入口:`@EnableAutoConfiguration` ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) // 核心入口 public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; } ``` - **关键设计**:通过`@Import`动态加载`AutoConfigurationImportSelector` #### 二、核心引擎:`AutoConfigurationImportSelector` ##### 1. 配置加载机制 ```java // 扫描所有spring.factories文件 protected List<String> getCandidateConfigurations( AnnotationMetadata metadata, AnnotationAttributes attributes ) { return SpringFactoriesLoader.loadFactoryNames( EnableAutoConfiguration.class, getBeanClassLoader() ); } ``` - **扫描路径**:`META-INF/spring.factories` - **配置键**:`org.springframework.boot.autoconfigure.EnableAutoConfiguration` ##### 2. 条件过滤流程 ```java // 执行条件过滤 private List<String> filter(List<String> configurations) { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); return new AutoConfigurationImportFilter() .filter(configurations, autoConfigurationMetadata); } ``` - **核心过滤器**: - `OnClassCondition`:检查类路径是否存在 - `OnBeanCondition`:检查Bean是否存在 - `OnWebApplicationCondition`:检查Web环境 ##### 3. 延迟加载机制 ```java // 实现DeferredImportSelector接口 public class AutoConfigurationImportSelector implements DeferredImportSelector, ... { // 延迟到所有常规@Configuration处理完成后执行 @Override public Class<? extends Group> getImportGroup() { return AutoConfigurationGroup.class; } } ``` #### 三、配置类解析:`AutoConfigurationGroup` ```java private static class AutoConfigurationGroup implements DeferredImportSelector.Group, ... { // 实际处理配置类的方法 @Override public void process(AnnotationMetadata metadata, DeferredImportSelector selector) { // 1. 加载候选配置 // 2. 条件过滤 // 3. 触发配置导入事件 } } ``` #### 四、典型配置类示例:`DataSourceAutoConfiguration` ```java @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration { } @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class }) protected static class PooledDataSourceConfiguration { } } ``` - **条件注解实战**: - `@ConditionalOnClass`:检测类路径存在性 - `@ConditionalOnMissingBean`:避免Bean重复定义 - `@EnableConfigurationProperties`:绑定配置属性 #### 五、关键设计模式 1. **工厂模式**: - `SpringFactoriesLoader`加载配置 2. **策略模式**: - `Condition`接口的不同实现 3. **观察者模式**: - `AutoConfigurationImportEvent`事件通知 4. **模板方法模式**: - `AutoConfigurationImportSelector`中的处理流程 #### 六、调试技巧 1. **查看加载的配置类**: ```properties # application.properties debug=true ``` 启动时将打印: ``` Positive matches: ----------------- DataSourceAutoConfiguration matched - @ConditionalOnClass found required classes 'javax.sql.DataSource'... ``` 2. **排除特定配置**: ```java @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值