Spring Boot 深入分析AutoConfigurationImportFilter自动化条件配置源码

 更新时间:2022年07月13日 09:33:51   作者:麦神-mirson  
这篇文章主要分析了Spring Boot AutoConfigurationImportFilter自动化条件配置源码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA 设置显示内存的使用情况和内存回收的方法

    IDEA 设置显示内存的使用情况和内存回收的方法

    这篇文章主要介绍了IDEA 设置显示内存的使用情况和内存回收的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • RabbitMQ简单队列实例及原理解析

    RabbitMQ简单队列实例及原理解析

    这篇文章主要介绍了RabbitMQ简单队列实例及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java网络编程UDP协议发送接收数据

    Java网络编程UDP协议发送接收数据

    这篇文章主要为大家详细介绍了Java网络编程UDP协议发送接收数据,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • java poi导入纯数字等格式问题及解决

    java poi导入纯数字等格式问题及解决

    这篇文章主要介绍了java poi导入纯数字等格式问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • 详解SPI在Dubbo中的应用

    详解SPI在Dubbo中的应用

    通过本文的学习,可以了解 Dubbo SPI 的特性及实现原理,希望对大家的开发设计有一定的启发性
    2021-06-06
  • java基础之反射和泛型以及注解

    java基础之反射和泛型以及注解

    这篇文章主要介绍了 java基础之反射和泛型以及注解的相关资料,需要的朋友可以参考下
    2017-07-07
  • springboot日志切面通用类实例详解

    springboot日志切面通用类实例详解

    这篇文章主要介绍了springboot日志切面通用类,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • 使用okhttp替换Feign默认Client的操作

    使用okhttp替换Feign默认Client的操作

    这篇文章主要介绍了使用okhttp替换Feign默认Client的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Spring框架构造注入type属性实例详解

    Spring框架构造注入type属性实例详解

    这篇文章主要介绍了Spring框架构造注入type属性实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java中使用开源库JSoup解析HTML文件实例

    Java中使用开源库JSoup解析HTML文件实例

    这篇文章主要介绍了Java中使用开源库JSoup解析HTML文件实例,Jsoup是一个开源的Java库,它可以用于处理实际应用中的HTML,比如常见的HTML格式化就可以用它来实现,需要的朋友可以参考下
    2014-09-09

最新评论