为了给组里的实习生妹妹讲怎样自界说Springboot的Starter,
我觉得需求让她先知道Springboot的主动安装机制,
但又感觉是不是得先让她了解Spring的SPI机制,
最后又觉得还有必要阐明一下一个@Configuration装备类在Spring中怎样被解析,这又会引出对ConfigurationClassPostProcessor的讲解,
完了,收不住了,到处都是常识盲区。
常识盲区脑图如下。
往期文章
一文学会Spring的@Configuration装备类解析
前言
何谓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机制的完结。
假如觉得本篇文章对你有帮助,求求你点个赞,加个保藏最后再点个关注吧。创造不易,感谢支撑!
本文正在参加「金石方案」