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

相关文章

  • Springboot实现Java邮件任务过程解析

    Springboot实现Java邮件任务过程解析

    这篇文章主要介绍了Springboot实现Java邮件任务过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Springboot+MyBatist实现前后台交互登陆功能方式

    Springboot+MyBatist实现前后台交互登陆功能方式

    这篇文章主要介绍了Springboot+MyBatist实现前后台交互登陆功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • JavaWeb中的组件Filter过滤器解析

    JavaWeb中的组件Filter过滤器解析

    这篇文章主要介绍了JavaWeb中的组件Filter过滤器解析,Filter过滤器是一种用于处理和转换数据的工具,常用于Web开发中,它可以在请求到达服务器之前或响应返回给客户端之前对数据进行预处理或后处理,需要的朋友可以参考下
    2023-10-10
  • Java压缩文件ZIP实例代码

    Java压缩文件ZIP实例代码

    这篇文章主要介绍了Java压缩文件ZIP实例代码,有需要的朋友可以参考一下
    2013-12-12
  • Java连接并操作Sedna XML数据库的方法

    Java连接并操作Sedna XML数据库的方法

    这篇文章主要介绍了Java连接并操作Sedna XML数据库的方法,较为详细的说明了Sedna XML数据库的原理与功能,并给出了基于java操作Sedna XML数据库的方法,需要的朋友可以参考下
    2015-06-06
  • java实现微信公众平台发送模板消息的示例代码

    java实现微信公众平台发送模板消息的示例代码

    这篇文章主要介绍了java实现微信公众平台发送模板消息的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • SpringBoot 拦截器返回false显示跨域问题

    SpringBoot 拦截器返回false显示跨域问题

    这篇文章主要介绍了SpringBoot 拦截器返回false显示跨域问题,文章围绕主题展开详细的内容介绍,需要的小伙伴可以参考一下
    2022-04-04
  • Java如何获取数组和字符串的长度(length还是length())

    Java如何获取数组和字符串的长度(length还是length())

    这篇文章主要介绍了Java如何获取数组和字符串的长度(length还是length()),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Spring框架学习常用注解汇总

    Spring框架学习常用注解汇总

    这篇文章主要为大家介绍了Spring框架学习中一些经常用的注解汇总及示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-10-10
  • 掷6面骰子6000次每个点数出现的概率

    掷6面骰子6000次每个点数出现的概率

    今天小编就为大家分享一篇关于掷6面骰子6000次每个点数出现的概率,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02

最新评论