1. AutoConfigurationImportFilter的作用

1.1 之前讲解了SpringBoot的Conditional的自动化条件配置,我们分析了内部是如何具体实现,在整个实现当中, 还有一个很重要的接口, AutoConfigurationImportFilter是它的前置调用, 它是一个过滤器接口,我们再做深入研究, 看下是如何控制处理这么多条件注解, 又是怎样过滤处理的,从性能效率又做了哪些处理?

1.2 AutoConfigurationImportFilter的源码:

@FunctionalInterface
public interface AutoConfigurationImportFilter {/*** Apply the filter to the given auto-configuration class candidates.* @param autoConfigurationClasses the auto-configuration classes being considered.* This array may contain {@code null} elements. Implementations should not change the* values in this array.* @param autoConfigurationMetadata access to the meta-data generated by the* auto-configure annotation processor* @return a boolean array indicating which of the auto-configuration classes should* be imported. The returned array must be the same size as the incoming* {@code autoConfigurationClasses} parameter. Entries containing {@code false} will* not be imported.*/boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);}

从说明可以看到,该类主要功能是过滤那些在spring.factories配置文件中定义的自动化配置项, 还有一个重要作用是在自动化配置类的字节码加载之前进行拦截过滤,提升处理效率, 节省资源开销。

2. AutoConfigurationImportFilter UML类图说明

从图中可以看到, AutoConfigurationImportFilter一共有三个实现类(OnBeanCondition、OnClasssCondition、OnWebApplicationCondition),三个类都是通过FilteringSpringBootCondition抽象父类间接实现,AutoConfigurationImportFilter在所有OnXXXCondition条件注解类的上层,这样大概就能看出它们的调用栈的关联关系, 经过研究代码, Spring Boot 会先调用AutoConfigurationImportFilter的match方法做过滤处理, 后面再通过loadBeanDefinitions触发Condition的matches方法做条件判断。

3. FilteringSpringBootCondition抽象类

FilteringSpringBootCondition是一个抽象类, 它继承SpringBootCondition,实现AutoConfigurationImportFilter的match接口, 内部调用抽象方法getOutcomes负责具体的过滤逻辑处理。

abstract class FilteringSpringBootCondition extends SpringBootConditionimplements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {// bean 工厂private BeanFactory beanFactory;// bean 加载器private ClassLoader beanClassLoader;// @Overridepublic boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {// 获取条件化判断报告, 用于记录处理结果ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);// 获取具体匹配处理结果, 由抽象方法getOutcomes负责具体实现ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);boolean[] match = new boolean[outcomes.length];// 遍历条件处理结果for (int i = 0; i < outcomes.length; i++) {match[i] = (outcomes[i] == null || outcomes[i].isMatch());if (!match[i] && outcomes[i] != null) {// 日志打印记录logOutcome(autoConfigurationClasses[i], outcomes[i]);if (report != null) {// 记录匹配处理结果report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);}}}return match;}...
}
  • 先获取ConditionEvaluationReport对象, 用于记录处理结果。

  • 调用getOutcomes方法, 这个一个抽象方法, 返回匹配处理结果, 由上面UML图中的OnXXXCondition等类负责具体实现。

  • 接下来创建match数组, 布尔值标记处理结果。

  • 下面还会调用logOutcome方法, 做日志打印处理。调用recordConditionEvaluation, 记录匹配结果。

除了match方法, FilteringSpringBootCondition下还有个 filter 方法。

Filter方法, protected修饰, 实际上会由OnXXXCondition的getOutcomes方法调用, 从UML关系图可以看到, 实际是由子类处理逻辑实现过程中调用。

protected List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,ClassLoader classLoader) {// 校验, 为空判断if (CollectionUtils.isEmpty(classNames)) {return Collections.emptyList();}// 记录match匹配结果List<String> matches = new ArrayList<>(classNames.size());// 遍历处理for (String candidate : classNames) {// 从指定的classLoader中加载class,再根据ClassNameFilter类型, 返回最终结果if (classNameFilter.matches(candidate, classLoader)) {matches.add(candidate);}}return matches;}

从源码可以看到,先做简单的为空判断, 具体则是通过classNameFilter的match方法做处理。
我们再看下ClassNameFilter的源码:

protected enum ClassNameFilter {// 两种类型, 当前存在优先, 如果classLoader中能够加载指定类, 返回truePRESENT {@Overridepublic boolean matches(String className, ClassLoader classLoader) {return isPresent(className, classLoader);}},// 缺失优先规则, 即便在classLoader中能够加载指定类, 也是返回falseMISSING {@Overridepublic boolean matches(String className, ClassLoader classLoader) {return !isPresent(className, classLoader);}};// 抽象方法, 有子类负责具体匹配逻辑实现 public abstract boolean matches(String className, ClassLoader classLoader);// 判断指定的类, 是否能够通过指定的classLoader加载public static boolean isPresent(String className, ClassLoader classLoader) {if (classLoader == null) {classLoader = ClassUtils.getDefaultClassLoader();}try {forName(className, classLoader);return true;}catch (Throwable ex) {return false;}}// 类的加载处理private static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {if (classLoader != null) {return classLoader.loadClass(className);}return Class.forName(className);}}

从中可以看出, 这里面有两种形式判断,一种是PRESENT, 另外一种是MISSING, 两种类型为相反逻辑, 通过isPresent方法做判断,里面则根据ClassLoader, 如果不为空, 则加载目标CLASS,处理没有报错, 则返回true值。

讲到这里, Filter的作用是什么?ClassNameFilter两种类型有什么意义? 我们举个例子说明, @ConditionalOnClass和@ConditionalOnMissingClass两个注解,判断条件是指定的CLASS是否存在。 如果采用ConditionalOnClass注解, 那么采用PRESENT存在优先规则, 如果采用ConditionalOnMissingClass注解, 那么采用MISSING缺失优先规则。

4. AutoConfigurationImportSelector类

再分析一下AutoConfigurationImportSelector这个类, 这是一个自动配置导入选择处理器,在AutoConfigurationImportFilter的match方法之前调用, 是属于上层调用。先由springFactores加载选择处理器, 主要包含OnClassCondition、OnWebApplicationCondition和OnBeanCondition等, 再做具体的条件判断处理。 我们了解下它的处理逻辑:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime();// 根据配置上下文, 获取所有需要处理的自动化配置类信息, 也就是所有的auotconfigration实现类String[] candidates = StringUtils.toStringArray(configurations);boolean[] skip = new boolean[candidates.length];boolean skipped = false;// 遍历处理, 通过getAutoConfigurationImportFilters方法, 获取springFactores中的选择处理器, 包含OnClassCondition、OnWebApplicationCondition和OnBeanCondition。for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {// 填充filter信息invokeAwareMethods(filter);// 获取filter的匹配结果boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {// 如果没有匹配, skip标记为trueskip[i] = true;// 清除该auotconfigration记录信息candidates[i] = null;skipped = true;}}}if (!skipped) {// 完全匹配, 直接返回configurations数据return configurations;}List<String> result = new ArrayList<>(candidates.length);for (int i = 0; i < candidates.length; i++) {if (!skip[i]) {// 记录需要处理的自动化配置信息result.add(candidates[i]);}}if (logger.isTraceEnabled()) {// 是否需要日志打印追踪int numberFiltered = configurations.size() - result.size();logger.trace("Filtered " + numberFiltered + " auto configuration class in "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");}return new ArrayList<>(result);}...
}

可以看到, 通过getAutoConfigurationImportFilters()加载过滤器, 在调用过滤器的match执行逻辑处理。条件匹配处理完成之后, 如果完全匹配, 则直接返回Configuration信息, 否则, 记录需要处理的自动化配置信息并做返回。 Configuration信息实际就是Spring Boot内置的一百多个自动化配置类:

这里也就是根据条件去过滤判断, 哪些AutoConfiguration符合规则, 哪些不符合规则, 只有符合规则的自动化配置类才会进入加载流程,实现对应的组件功能。

5. 总结

AutoConfigurationImportFilter是Spring Boot条件化注解的核心过滤器接口,这个类在启动的时候通过SPI机制实现,在Spring Boot的条件化配置中会进行回调. 基于Conditional的自动化配置主要流程就分析到这里, 细节上就不再赘述, 大家有空可以再跟踪源码深入研究,了解更为细节的自动化配置的处理逻辑。

【Spring Boot 源码研究 】- 自动化装配条件化配置AutoConfigurationImportFilter剖析相关推荐

  1. 【Spring Boot 源码研究 】- 自动化装配机制核心注解剖析

    1. 自动化装配介绍 Spring Boot针对mvc做了大量封装,简化开发者的使用,内部是如何管理资源配置,Bean配置,环境变量配置以及启动配置等? 实质是SpringBoot做了大量的注解封装, ...

  2. spring boot 源码解析15-spring mvc零配置

    前言 spring boot 是基于spring 4 的基础上的一个框架,spring 4 有一个新特效–>基于java config 实现零配置.而在企业的实际工作中,spring 都是和sp ...

  3. 【Spring Boot 源码研究 】- 自动化装配条件化配置Conditional剖析

    1. Spring Boot Condition功能与作用 @Conditional是基于条件的自动化配置注解, 由Spring 4框架推出的新特性. 在一个服务工程, 通常会存在多个配置环境, 比如 ...

  4. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  5. 【赠书福利】掘金爆火小册同名《Spring Boot源码解读与原理剖析》正式出书了!...

    关注我们丨文末赠书 承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解 ...

  6. 实战:Spring Boot源码解读与原理分析

    承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解读与原理剖析> ...

  7. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  8. 【细读Spring Boot源码】重中之重refresh()

    前言 版本:spring-boot-2.7.3 | spring-context-5.3.22 在Spring Boot启动过程中[细读Spring Boot源码]启动步骤 主流程详情7中applic ...

  9. 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现

    概述 我们知道 Spring Boot 能够创建独立的 Spring 应用,内部嵌入 Tomcat 容器(Jetty.Undertow),让我们的 jar 无需放入 Servlet 容器就能直接运行. ...

最新文章

  1. 经典的Java基础面试题集锦
  2. TCP/IP 笔记 1.3 IP:网际协议
  3. 两种常见的点云配准方法ICPNDT
  4. 关于bash的执行过程
  5. 使用hibernate和struts2实现分页功能
  6. cvs update 用法_WinCVS的配置与使用方法
  7. 【theano-windows】学习笔记十一——theano中与神经网络相关函数
  8. Quick cocos2dx-Lua(V3.3R1)学习笔记(十)-----搭建安卓打包环境,用官方示例anysdk生成apk运行...
  9. mysql中ak替换键_数据库:唯一性约束_alternate key(替换键) mySQL Oracle 数据库 ak 唯一性约束...
  10. Linux内核分析 读书笔记 (第四章)
  11. 产品研发过程管理专题——软件工程(软件目的需求开发与管理)
  12. webpack打包初体验
  13. 7 netsnmp安装window_Linuxfx 10.2,一款来自巴西的Window操作系统,“山寨”出了高度...
  14. mac 谷歌浏览器 跨域访问
  15. iOS逆向一:数字签名苹果应用双重签名原理应用重签名
  16. 如何运营高效的社群?
  17. mysql通过字段值查询表名
  18. javascript原生初级到非凡-姜威-专题视频课程
  19. Brave Game(bash game-巴什博弈)
  20. ADM周期-阶段目标说明

热门文章

  1. linux 跳板机 外网,linux 基于ssh创建跳板机
  2. Quartus II 13.0无modelsim进行仿真(用自带仿真器)
  3. Doocker ubuntu 16.04 学习总结(二)-使用docker搭建web服务器
  4. DBCO-PEG3-NHS ester,2163772-16-3,二苯并环辛炔-三聚乙二醇-琥珀酰亚胺酯
  5. MATLAB与STK互联1:建立STK场景并保存(补丁)
  6. 特许权使用费(Royalty Fee)
  7. 每日一题【55】导数-复杂的题干=简单的思路
  8. 微软的官方升级网站系统--Windows update
  9. Linux 解压tar.gz文件到指定目录
  10. 修改ORACLE服务名