Spring Boot 深入分析AutoConfigurationImportFilter自动化条件配置源码
作者:麦神-mirson
1. AutoConfigurationImportFilter的作用
之前讲解了SpringBoot的Conditional的自动化条件配置,我们分析了内部是如何具体实现,在整个实现当中, 还有一个很重要的接口, AutoConfigurationImportFilter是它的前置调用, 它是一个过滤器接口,我们再做深入研究, 看下是如何控制处理这么多条件注解, 又是怎样过滤处理的,从性能效率又做了哪些处理?
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 SpringBootCondition implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware { // bean 工厂 private BeanFactory beanFactory; // bean 加载器 private ClassLoader beanClassLoader; // @Override public 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中能够加载指定类, 返回true PRESENT { @Override public boolean matches(String className, ClassLoader classLoader) { return isPresent(className, classLoader); } }, // 缺失优先规则, 即便在classLoader中能够加载指定类, 也是返回false MISSING { @Override public 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标记为true skip[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自动化条件配置源码的文章就介绍到这了,更多相关Spring Boot AutoConfigurationImportFilter内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!