springboot starter原理简单介绍

原理简单概述

  • springboot 各种starter简单来说就是为了加载各种依赖包中配置类@Configuration,这些配置类必须在模块的resources/META-INF 目录下spring.factories文件中键为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值中声明
  • 因为我们不可能在启动类中声明扫描的包包含所有依赖包的配置类@Configuration,那样不现实。所以spring就提供了一种机制来加载这些配置类@Configuration。只要能够读取到这些配置类,那么就和平常自己在项目中声明的配置类@Configuration使用一样的方式来解析

总结:因为不想声明各种依赖包中配置类@Configuration对应的扫描的包,因为太多太乱,又想让spring管理这些配置类@Configuration,所以使用了其它方式可以读取这些配置类。

源码解析

(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

spring @Import介绍这篇文章中已经说明了@Import的用法以及AutoConfigurationImportSelector属于DeferredImportSelector(延期的ImportSelector)类型。下面的说明全部基于此。

对于DeferredImportSelector,会在spring处理完我们自定义的@Configuration bean 后运行(为啥呢?因为自动配置类中大都会有@Condition,就比如用户若是已经定义了某个bean,那么自动配置的就不生效)。这里需要对ConfigurationClassPostProcessor类作为BeanFactoryPostProcessor都做了哪些事情要有了解,这里不做介绍。可以看我之前
ConfigurationClassPostProcessor这篇文章。这里只看具体代码。

ConfigurationClassParser#parse简化后代码,最后一行代码就是处理DeferredImportSelector。

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
		}
		this.deferredImportSelectorHandler.process();
	}

spring @Import介绍这篇文章中已经说了对于 DeferredImportSelector,之前就是被包装成DeferredImportSelectorHolder然后放到deferredImportSelectorHandler的一个list集合中,这里就是直接解析deferredImportSelectorHandler的这个集合就可以了。

DeferredImportSelectorHandler#process

		public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
这里就是声明了一个DeferredImportSelectorGroupingHandler,然后循环调用其register方法处理这些DeferredImportSelectorHolder。这里先看register方法。
	private class DeferredImportSelectorGroupingHandler {
		private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
		private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

		public void register(DeferredImportSelectorHolder deferredImport) {
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			//1
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}
  • 获取DeferredImportSelectorHolder 中的ImportSelector所属的组。下面是AutoConfigurationImportSelector获取组源码。
	@Override
   public Class<? extends Group> getImportGroup() {
   	return AutoConfigurationGroup.class;
   }
  • 从grouping属性中获取对应的DeferredImportSelectorGrouping ,没有则创建,然后将该DeferredImportSelectorHolder 添加到该组中。注意这里的createGroup方法,后面会用到该方法返给DeferredImportSelectorGrouping 这个参数。简单理解就是spring把DeferredImportSelector分了组。这里把同一组的收集了起来。
  • 最后一行代码将注解属性和ConfigurationClass映射,需要结合后续处理一起看(1处代码)
register完了之后才是真正的处理handler.processGroupImports()
		public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
				});
			}
		}

可以看到这里就是按照register方法分组后来处理的。 grouping.getImports()源码如下

		public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
  • 使用group成员变量循环处理DeferredImportSelectorHolder 。上面说了AutoConfigurationImportSelector的group为AutoConfigurationGroup,所以上述register方法中createGroup会创建AutoConfigurationGroup的实例,也就是这里的group成员变量的值

AutoConfigurationGroup#process源码

	public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
	//AutoConfigurationEntry就包含那些自动配置类的全类名,当然还包含排除的类。
		AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(annotationMetadata);
		this.autoConfigurationEntries.add(autoConfigurationEntry);
		for (String importClassName : autoConfigurationEntry.getConfigurations()) {
			this.entries.putIfAbsent(importClassName, annotationMetadata);
		}
	}

获取AutoConfigurationEntry会调用AutoConfigurationImportSelector#getAutoConfigurationEntry方法,该方法后续会有下面的调用

	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
				
	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

是不是很熟悉了。看到了EnableAutoConfiguration.class和SpringFactoriesLoader.loadFactoryNames,对于SpringFactoriesLoader.loadFactoryNames不熟悉的可以看其它文章或者我的这篇文章springboot2.1.7启动分析(一)SpringApplication实例化,这里就不在说明。

接着看grouping.getImports()源码最后一行代码:return this.group.selectImports()部分源码

		public Iterable<Entry> selectImports() {
		...
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

简单来说就是把配置类包装成Entry返回.

		class Entry {
			private final AnnotationMetadata metadata;
			private final String importClassName;
			public Entry(AnnotationMetadata metadata, String importClassName) {
				this.metadata = metadata;
				this.importClassName = importClassName;
			}

Entry中的metadata就是上面register方法最后一行代码configurationClassesd(map类型)中键是一致的,所以这一获取到的ConfigurationClass 都是一样的。都是DeferredImportSelectorHolder 中的ConfigurationClass 。

				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
				});

processImports方法在spring @Import介绍中已经说明过,该方法的第三个参数就是候选的@import对应的value值。这里正是把entry.getImportClassName()也就是配置类包装成该参数。这里相当于又是对于@import值的处理,因为这些配置类应该都不是ImportSelector或ImportBeanDefinitionRegistrar类型,将会作为@Configuration类处理。

总结

这里只是简单的说明了spring是何时以及怎么读取这些自动配置类,以及将其作为@Configuration类处理,中间省略了很多细节,这篇文章只是关注那些生效的自动配置类是如何被spring加载的。转载:自动装配的奥秘这篇文章讲的更加详细。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值