一文搞定Springboot的自动装配机制
为了给组里的实习生妹妹讲如何自定义Springboot的Starter,
我觉得需要让她先知道Springboot的自动装配机制,
但又感觉是不是得先让她了解Spring的SPI机制,
最后又觉得还有必要说明一下一个@Configuration配置类在Spring中如何被解析,这又会引出对ConfigurationClassPostProcessor的讲解,
完了,收不住了,到处都是知识盲区。
知识盲区脑图如下。
前言
何谓Springboot的自动装配,简要概括就是:引入第三方组件的starter包后能够自动将第三方组件的bean加载到IOC容器中供用户使用。
自动装配的机制是Springboot提供的,因此第三方组件的starter包在编写的时候,就需要根据Springboot的自动装配的规则来编写starter包,那么这里的规则概括如下。
- starter包需要在/META-INF/目录下提供spring.factories文件;
- spring.factories文件中以Key-Values的形式来提供需要Springboot去加载的类的全限定名。这里的Key就是Springboot中各种扩展点的全限定名,比如org.springframework.boot.autoconfigure.EnableAutoConfiguration,然后Values就是starter包中提供的扩展点的所有类的全限定名,以逗号隔开。
只要第三方组件的starter包按照上述规则来编写,那么Springboot就能够将starter包提供的各种扩展点的类进行加载,例如下面的这个spring.factories文件。
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
复制代码
啥意思呢,其实就是第三方组件的starter包在告诉Springboot:嘿,亲爱的Springboot,我提供了一个监听器,它的全限定名是org.springframework.boot.autoconfigure.BackgroundPreinitializer,拜托你去加载它;嘿,亲爱的Springboot,我提供了多个自动配置类,它们的全限定名是org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,org.springframework.boot.autoconfigure.aop.AopAutoConfiguration和org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,拜托你去加载它们。
Springboot的自动装配机制解释起来很简单,使用起来也巨方便,关于自动装配的具体使用,将在后续的自定义starter包一文中进行演示。
本文的重点是Springboot的自动装配机制的底层原理,废话不多说,开造。
Springboot版本:2.4.1
正文
一. 认识@SpringBootApplication注解
我们新建一个Springboot应用,都需要一个启动类,然后在启动类上添加@SpringBootApplication注解,那么来看一下@SpringBootApplication注解的组成。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {}
复制代码
也就是说@SpringBootApplication是由@SpringBootConfiguration,@EnableAutoConfiguration和@ComponentScan组成的复合注解,下面是对这三个注解的说明。
- @SpringBootConfiguration表明Springboot启动类是一个配置类;
- @ComponentScan注解会将指定路径下的被特定注解修饰的类加载为Spring中的bean,这些特定注解为@Component,@Controller,@Service,@Repository和@Configuration注解;
- @EnableAutoConfiguration注解用于开启Springboot的自动装配。
也就是说,相当于我们在Springboot应用的启动类上添加了一个叫做@EnableAutoConfiguration的注解,从而开启了自动装配功能。
那么再看一下@EnableAutoConfiguration注解的组成。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
复制代码
@EnableAutoConfiguration注解也是一个复合注解,主要功能由@AutoConfigurationPackage注解和@Import注解实现,那么肯定的,自动装配,就是这两个注解实现的。
二. @AutoConfigurationPackage注解
先给出结论。
@AutoConfigurationPackage注解作用在Springboot启动类上,会向Spring容器注册一个类型为AutoConfigurationPackages.BasePackages的bean,这个bean中保存了Springboot启动类的包路径,后续Springboot就会扫描这个包路径下由@Component,@Controller,@Service,@Repository和@Configuration注解修饰的类。
下面开始分析原理。
@AutoConfigurationPackage注解的功能由@Import(AutoConfigurationPackages.Registrar.class) 实现,所以看一下AutoConfigurationPackages.Registrar的类图,如下所示。
所以AutoConfigurationPackages.Registrar实际是一个ImportBeanDefinitionRegistrar,那么在Spring容器启动过程中,会调用到AutoConfigurationPackages.Registrar的registerBeanDefinitions() 方法向注册表注册一个BeanDefinition,registerBeanDefinitions() 方法如下所示。
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
复制代码
在registerBeanDefinitions() 方法中调用了AutoConfigurationPackages的register() 方法,register() 方法的第二个参数是通过new PackageImports(metadata).getPackageNames() 获取,其实就是将被@AutoConfigurationPackage注解修饰的类的包路径返回。
现在看一下register() 方法的实现,如下所示。
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
复制代码
register() 方法中会将包路径packageNames封装成一个BasePackagesBeanDefinition,然后注册到注册表中。下面看一下BasePackagesBeanDefinition的构造方法,如下所示。
BasePackagesBeanDefinition(String... basePackages) {
setBeanClass(BasePackages.class);
setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
addBasePackages(basePackages);
}
复制代码
BasePackagesBeanDefinition的构造方法中,将beanClass设置为了BasePackages.class,那么后续会基于BasePackagesBeanDefinition向容器注册一个类型为BasePackages的bean,并且BasePackages的bean中的packages字段是一个包路径的集合。
最后说明一下,BasePackagesBeanDefinition和BasePackages都是AutoConfigurationPackages的静态内部类,类图如下。
三. AutoConfigurationImportSelector
@EnableAutoConfiguration注解实现自动装配主要就是依靠@Import(AutoConfigurationImportSelector.class),下面先看一下AutoConfigurationImportSelector的类图。
所以AutoConfigurationImportSelector是一个DeferredImportSelector,那么在ConfigurationClassParser的parse(Set<BeanDefinitionHolder> configCandidates) 方法中会开启处理AutoConfigurationImportSelector的逻辑。
parse(Set<BeanDefinitionHolder> configCandidates) 如下所示。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 初始配置类对应的BeanDefinition为AnnotatedGenericBeanDefinition
// AnnotatedGenericBeanDefinition实现了AnnotatedBeanDefinition接口
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 延迟处理DeferredImportSelector
this.deferredImportSelectorHandler.process();
}
复制代码
deferredImportSelectorHandler字段类型为DeferredImportSelectorHandler,是ConfigurationClassParser的一个内部类,其process() 方法如下所示。
public void process() {
// DeferredImportSelectorHolder对ConfigurationClass和DeferredImportSelector进行了简单封装
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
// DeferredImportSelectorGroupingHandler是ConfigurationClassParser的内部类
// 用于分组处理DeferredImportSelector,这里的组指DeferredImportSelector的内部接口Group
// Group接口的作用是用于对不同ImportSelector的导入结果进行分组
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 对deferredImports进行排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 分组地将DeferredImportSelector注册到DeferredImportSelectorGroupingHandler中
deferredImports.forEach(handler::register);
// 调用DeferredImportSelectorGroupingHandler的processGroupImports()方法分组处理DeferredImportSelector
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
复制代码
DeferredImportSelectorHolder是对ConfigurationClass和DeferredImportSelector进行的简单封装,在DeferredImportSelectorHandler#process方法中,先获取到所有的DeferredImportSelector,然后创建DeferredImportSelectorGroupingHandler用于处理DeferredImportSelector,DeferredImportSelectorGroupingHandler会先注册DeferredImportSelector到DeferredImportSelectorGroupingHandler的groupings字段中,然后调用DeferredImportSelectorGroupingHandler#processGroupImports方法处理groupings字段中的DeferredImportSelector,而且整个处理是按组进行处理的,这里的组其实就是DeferredImportSelector的内部接口Group,DeferredImportSelector接口的实现类实现的getImportGroup() 方法就需要返回一个Group接口的实现类的Class对象,通常如下所示。
public class MyDeferredImportSelector implements DeferredImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[0];
}
public Predicate<String> getExclusionFilter() {
return null;
}
public Class<? extends Group> getImportGroup() {
// 返回MyGroup的Class对象
return MyGroup.class;
}
// 定义一个内部类并且实现DeferredImportSelector的内部接口Group
private static class MyGroup implements DeferredImportSelector.Group {
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
}
@Override
public Iterable<Entry> selectImports() {
return new ArrayList<>();
}
}
}
复制代码
那么现在看一下DeferredImportSelectorGroupingHandler是如何注册DeferredImportSelector到DeferredImportSelectorGroupingHandler的groupings字段中的。
DeferredImportSelectorGroupingHandler#register方法如下所示。
public void register(DeferredImportSelectorHolder deferredImport) {
// 获取Group接口的实现类的Class对象
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 将Group接口的实现类进行实例化,然后封装成DeferredImportSelectorGrouping对象
// 以Group接口的实现类的Class对象为键,DeferredImportSelectorGrouping对象为值,注册到groupings字段中
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 为当前Group接口的实现类对应的DeferredImportSelectorGrouping添加DeferredImportSelector
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
复制代码
DeferredImportSelectorGroupingHandler的groupings字段是一个map结构,键是Group接口的实现类的Class对象,值是DeferredImportSelectorGrouping对象,而DeferredImportSelectorGrouping对象是对Group接口的实现类的实例和DeferredImportSelector的封装,那么上面的DeferredImportSelectorGroupingHandler#register方法其实就是将相同组的DeferredImportSelector全部放到同一个DeferredImportSelectorGrouping对象中,然后再将DeferredImportSelectorGrouping对象注册到DeferredImportSelectorGroupingHandler的groupings字段中。
那么怎么才算是相同组的DeferredImportSelector,其实就是DeferredImportSelector的getImportGroup() 方法返回相同的Class对象。
现在再看一下DeferredImportSelectorGroupingHandler是如何处理DeferredImportSelector的,DeferredImportSelectorGroupingHandler#processGroupImports方法如下所示。
public void processGroupImports() {
// 遍历每一个DeferredImportSelectorGrouping,即按组来处理DeferredImportSelector
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 通过grouping.getImports()获取到当前组里所有DeferredImportSelector导入的类
// 然后将每个导入的类封装成一个ConfigurationClass然后进行解析
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// 调用到ConfigurationClassParser的processImports()方法进入递归解析流程
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
复制代码
DeferredImportSelectorGroupingHandler#processGroupImports方法中是分组进行处理,每次将一组中的所有DeferredImportSelector导入的类获取出来,然后将每个导入的类封装成ConfigurationClass,后续就是处理ConfigurationClass的逻辑。
现在看一下DeferredImportSelectorGrouping的getImports() 方法的具体实现,如下所示。
public Iterable<Group.Entry> getImports() {
// 先遍历当前组的每个DeferredImportSelector,并通过Group接口的实现类的process()方法进行处理
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 然后调用Group接口的实现类的selectImports()方法返回所有需要导入的类
// 这些需要导入的类会被封装成Group.Entry对象
return this.group.selectImports();
}
复制代码
DeferredImportSelectorGrouping的getImports() 方法会先调用Group接口的实现类的process() 方法处理组里的每个DeferredImportSelector,然后再调用Group接口的实现类的selectImports() 方法获取所有需要导入的类,每个需要被导入的类会被封装成Group.Entry对象并返回。先分析AutoConfigurationImportSelector中的组对象AutoConfigurationGroup的process() 方法,实现如下。
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 调用AutoConfigurationImportSelector的getAutoConfigurationEntry()方法获取所有需要自动装配的组件的配置类的全限定名
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
// 添加到组对象AutoConfigurationGroup的autoConfigurationEntries集合中
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
复制代码
继续分析AutoConfigurationImportSelector的getAutoConfigurationEntry() 方法,看一下所有需要自动装配的组件的配置类的全限定名是如何获取的。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration注解的元数据属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 将需要自动装配的组件的配置类的全限定名获取出来
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复的组件
configurations = removeDuplicates(configurations);
// 去除被排除的组件
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 去除依赖项不满足的组件
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回剩余的需要自动装配的组件的配置类的全限定名
return new AutoConfigurationEntry(configurations, exclusions);
}
复制代码
AutoConfigurationImportSelector的getAutoConfigurationEntry() 方法是先将所有需要自动装配的组件的配置类的全限定名获取出来,然后进行去重和条件过滤。AutoConfigurationImportSelector的getAutoConfigurationEntry() 方法中,最重要的就是getCandidateConfigurations() 方法,其实现如下。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass()的返回值是EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
复制代码
SpringFactoriesLoader是Spring的SPI机制的实现类,Springboot能够完成组件自动装配就是依赖的SpringFactoriesLoader,下面分析一下SpringFactoriesLoader#loadFactoryNames方法,如下所示。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 这里factoryTypeName为org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
// loadSpringFactories()方法会先根据ClassLoader从缓存中获取需要自动装配的组件信息
// 获取不到则使用ClassLoader将classpath下所有jar包的所有META-INF/spring.factories文件中的信息加载
// loadSpringFactories()方法获取出来的数据是一个Map
// 形式为Map[factoryTypeName, List[自动装配的组件的配置类的全限定名]]
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
复制代码
SpringFactoriesLoader#loadFactoryNames方法中调用了loadSpringFactories() 方法基于ClassLoader来将需要自动装配的组件的配置类的全限定名获取出来,所有classpath下的jar包中的META-INF/spring.factories文件都会被扫描。loadSpringFactories() 方法实现如下。
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 先根据ClassLoader从缓存中获取
// Map[factoryTypeName, List[自动装配的组件的配置类的全限定名]]
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 将所有META-INF/spring.factories文件路径获取出来
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 将spring.factories文件的内容读取成Properties,Properties本质是一个HashTable
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 将Properties的内容添加到result
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
// 缓存到cache中
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
复制代码
下面再给出spring-boot-autoconfigure包下的spring.factories文件的部分内容,就能够知道上面的loadSpringFactories() 方法的返回值具体是什么结构。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
......
复制代码
因此loadSpringFactories() 方法的返回值map的键可以是上述的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,或者org.springframework.boot.autoconfigure.EnableAutoConfiguration等,值就是一个全限定名的集合。所以在SpringFactoriesLoader#loadFactoryNames方法中能够通过loadSpringFactories() 方法的返回值拿到org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的需要自动装配的配置类的全限定名的集合。
现在继续分析AutoConfigurationImportSelector中的组对象AutoConfigurationGroup的selectImports() 方法,如下所示。
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
// 将组中每个DeferredImportSelector导入的类的全限定名获取出来
// 这里导入的类就是需要自动装配的组件的配置类
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
复制代码
因为组里会有多个DeferredImportSelector,而每个DeferredImportSelector会在AutoConfigurationGroup的process() 方法中生成一个AutoConfigurationEntry,所以上面的方法就是将每个DeferredImportSelector对应的AutoConfigurationEntry中的全限定名的集合合并到一个集合中并返回。
至此,AutoConfigurationImportSelector整体的一个处理流程分析完毕。
整个分析流程很长,但是如果耐着性子看完,会发现整个流程可以用下图进行概括。
总结
Springboot的自动装配功能由@EnableAutoConfiguration注解提供。
而@EnableAutoConfiguration注解的功能主要由以下两部分实现。
一. @AutoConfigurationPackage
@AutoConfigurationPackage注解作用在Springboot启动类上,会向Spring容器注册一个类型为AutoConfigurationPackages.BasePackages的bean,这个bean中保存了Springboot启动类的包路径,后续Springboot就会扫描这个包路径下由@Component,@Controller,@Service,@Repository和@Configuration注解修饰的类。
二. @Import(AutoConfigurationImportSelector.class)
- @Import(AutoConfigurationImportSelector.class) 会通过AutoConfigurationImportSelector延迟且分组的向Spring容器导入需要自动装配的组件的配置类,从而在解析这些配置类的时候能够将自动装配的组件的bean注册到容器中;
- 所谓的延迟,是因为AutoConfigurationImportSelector实现了DeferredImportSelector接口,其逻辑会在Springboot启动类被解析完毕后才会执行;
- 所谓的分组,是因为处理DeferredImportSelector是一组一组的进行的,只要DeferredImportSelector的实现类实现的getImportGroup() 方法返回的Class对象一样,那么这样的DeferredImportSelector的实现类就属于同一组;
- AutoConfigurationImportSelector获取到需要自动装配的组件的配置类的全限定名,是通过SpringFactoriesLoader完成的,而SpringFactoriesLoader就是Spring中的SPI机制的实现。
还没有评论,来说两句吧...