目录
一、简介
1)、@SpringBootConfiguration
2)、@ComponentScan
3)、@EnableAutoConfiguration
二、@AutoConfigurationPackage
三、@Import(AutoConfigurationImportSelector.class)
1)、AutoConfigurationImportSelector介绍
2)、自动配置流程
2-1)、ConfigurationClassParser#parse()
2-2)、this.deferredImportSelectorHandler.process()
2-2-1)、第一步:register()
2-2-2)、第二步:processGroupImports()
本篇源码基于spring-boot-2.1.0.RELEASE版本进行分析,各个版本可能存在一些差别。
一、简介
SpringBoot的启动从main()方法开始:
@SpringBootApplication
public class SampleTomcatApplication {
public static void main(String[] args) {
SpringApplication.run(SampleTomcatApplication.class, args);
}
}
在主启动类上,我们只需要加上@SpringBootApplication注解,就能成功启动springboot应用了,那么我们就从注解入手,看下它是如何帮我们配置好所需要的一些环境的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// @SpringBootConfiguration内部使用@Configuration修饰,标记启动类是一个Spring的配置类
@SpringBootConfiguration
// @EnableAutoConfiguration: 实现自动装配的核心注解. 内部包含两个注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)
@EnableAutoConfiguration
// 包扫描:扫描启动类所在的包以及子包所有Bean组件并注册到IOC容器中
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
// SpringBoot项目启动的核心注解
// @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
// 等同于EnableAutoConfiguration注解的exclude属性
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
// 等同于EnableAutoConfiguration注解的excludeName属性
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
// 等同于ComponentScan注解的basePackages属性
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
// 等同于ComponentScan注解的basePackageClasses属性
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootApplication 使用三个注解修饰:
1)、@SpringBootConfiguration
标记启动类是一个Spring的配置类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
// 相当于@SpringBootConfiguration就是@Configuration
public @interface SpringBootConfiguration {
}
2)、@ComponentScan
扫描启动类所在的包以及子包所有Bean组件并注册到IOC容器中。
3)、@EnableAutoConfiguration
实现自动装配的核心注解,内部包含两个注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// 使用@Import注解导入AutoConfigurationImportSelector类,实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 排除特定的自动配置类,使它们永远不会被应用
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* 排除特定的自动配置类名称,使其永远不会被应用
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
springboot的自动装配就是依靠@EnableAutoConfiguration这个注解实现的,而@EnableAutoConfiguration又是由下面两部分组成:
1、@AutoConfigurationPackage
2、@Import(AutoConfigurationImportSelector.class)
二、@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 向容器中导入了AutoConfigurationPackages.Registrar组件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
可以看到,@AutoConfigurationPackage注解的功能是由@Import注解实现的,它是Spring框架的底层注解,它的作用就是给容器中导入某个组件类AutoConfigurationPackages.Registrar,我们看下这是个什么组件?
@AutoConfigurationPackage注解的作用是将添加该注解的类所在的package作为自动配置package进行管理。也就是说当SpringBoot应用启动时默认会将启动类所在的package作为自动配置的package,这样主程序类所在包及所有子包下的组件就会被扫描到spring容器中。
// 实现了ImportBeanDefinitionRegistrar、DeterminableImports接口,重写了registerBeanDefinitions()、determineImports()方法
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// 注册bean定义
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
// 返回一组需要注入的对象
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
AutoConfigurationPackages.Registrar实现了ImportBeanDefinitionRegistrar,并重写了registerBeanDefinitions()注册bean定义信息的方法,这样在刷新IOC容器,即执行refresh()方法,在invokeBeanFactoryPostProcessors(beanFactory)这一步,会执行BeanDefinitionRegistryPostProcessor这个后置处理器的增强方法时,执行loadBeanDefinitions()加载bean定义信息,就会扫描到AutoConfigurationPackages.Registrar这个类,然后就会回调AutoConfigurationPackages.Registrar中的registerBeanDefinitions()方法:
register(registry, new PackageImport(metadata).getPackageName());
// 注册自动包规则的bean定义信息
public static void register(BeanDefinitionRegistry registry, String... packageNames) { // sample.tomcat
// 判断bean定义信息注册表中是否存在org.springframework.boot.autoconfigure.AutoConfigurationPackages
if (registry.containsBeanDefinition(BEAN)) {
// 如果该bean已经注册,则将要注册包名称添加进去
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
}
else { // 如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
// 创建简单类型的bean定义信息GenericBeanDefinition
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
// bean类 : AutoConfigurationPackages$BasePackages
beanDefinition.setBeanClass(BasePackages.class);
// bean实例构造函数第一个参数 : 被使用类所在包名 (比如@SpringBootApplication注解所在类的包名)
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
// bean 角色 : 基础设施
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册bean定义信息, bean名称 : org.springframework.boot.autoconfigure.AutoConfigurationPackages
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
在本例中,SampleTomcatApplication在sample.tomcat包中,所以自动配置package为sample.tomcat。
@AutoConfigurationPackage和@ComponentScan的作用有什么区别?
- @AutoConfigurationPackage :是为了给组件开发者使用的,组件开发者在一个路径下面有多个自动配置的类想加载,这个注解就不用对每个类单独添加 @Import 了,直接引入包路径更方便。
- @ComponentScan :是为了用组件的开发者准备的,方便你对包路径进行自定义,比如你的一些 Bean 跟 SpringBootApplication 不在一个路径下面,或者多个不同路径下面,这个就起到作用了。
以上面的示例来说,其实@AutoConfigurationPackage就相当于在启动类加上@ComponentScan(basePackages = "sample.tomcat")。
三、@Import(AutoConfigurationImportSelector.class)
1)、AutoConfigurationImportSelector介绍
@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)向容器中导入了AutoConfigurationImportSelector组件,它实现了DeferredImportSelector,并间接实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合。
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
//...
}
我们先来看下AutoConfigurationImportSelector的类图:
AutoConfigurationImportSelector实现的是DeferredImportSelector接口,而不是ImportSelector接口,两者的执行时间是不一样的:
- 1、ImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理;
- 2、DeferredImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理;
2)、自动配置流程
前面介绍到,AutoConfigurationImportSelector实现的是DeferredImportSelector接口,那么下面我们来看看它在哪里发挥作用的。
回到启动类的main()方法:
SpringApplication.run(SampleTomcatApplication.class, args);
执行run()方法:
// 运行 Spring 应用程序,创建并刷新一个新的ApplicationContext
public ConfigurableApplicationContext run(String... args) {
// 创建一个任务执行观察器,用于统计run启动过程花了多少时间
StopWatch stopWatch = new StopWatch();
// 记录开始时间
stopWatch.start();
ConfigurableApplicationContext context = null;
// exceptionReporters集合用来存储异常报告器,用来报告SpringBoot启动过程的异常
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置了一个名为java.awt.headless的系统属性, 其实是想设置该应用程序,即使没有检测到显示器,也允许其启动. 对于服务器来说,是不需要显示器的,所以要这样设置.
configureHeadlessProperty();
// 从spring.factories配置文件中加载到EventPublishingRunListener对象并赋值给SpringApplicationRunListeners
// EventPublishingRunListener对象主要用来发布SpringBoot启动过程中内置的一些生命周期事件,标志每个不同启动阶段
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布启动事件
listeners.starting();
try {
// 创建ApplicationArguments对象,封装了args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境,包括系统变量、环境变量、命令行参数、默认变量等
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置需要忽略的BeanInfo信息
configureIgnoreBeanInfo(environment);
// 启动时控制台打印Banner
Banner printedBanner = printBanner(environment);
// 根据不同类型创建不同类型的spring容器ApplicationContext应用程序上下文
context = createApplicationContext();
// 加载spring.factories配置文件配置的异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文,刷新容器前的一些操作
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文。完成Spring IOC容器的初始化
refreshContext(context);
// 在刷新上下文后调用的钩子,这个方法是一个模板方法
afterRefresh(context, applicationArguments);
// 停止记录执行时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 事件广播,启动完成了
listeners.started(context);
// 执行ApplicationRunner、CommandLineRunner的run方法,实现spring容器启动成功后需要执行的一些逻辑
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
上述代码是springboot应用启动的核心流程,我们重点关注refreshContext(context),也就是刷新Spring的IOC容器方法,自动配置就是在里面实现的。
refreshContext()方法实际上是调用org.springframework.context.support.AbstractApplicationContext#refresh,这个方法是IOC容器刷新的核心方法,主要包括12个步骤:
- a、prepareRefresh():容器刷新前的一些预处理工作;
- b、obtainFreshBeanFactory():创建DefaultListableBeanFactory工厂,给bean工厂设置一些属性,加载配置文件信息,封装成bean定义信息;
- c、prepareBeanFactory(beanFactory):设置bean工厂的一些属性,如添加一些BeanPostProcessor增强器等
- d、postProcessBeanFactory(beanFactory):模板方法,留给子类扩展实现;
- e、invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor的postProcessBeanFactory()增强方法;
- f、registerBeanPostProcessors(beanFactory):注册BeanPostProcessor增强器,注意这里只是注册,真正是在初始化阶段的前后执行;
- g、initMessageSource():初始化MessageSource,国际化处理;
- h、initApplicationEventMulticaster():初始化事件多播器;
- i、onRefresh():模板方法,留给子类扩展实现;
- j、registerListeners():注册一些监听器;
- k、finishBeanFactoryInitialization(beanFactory):完成非懒加载的单例bean对象的实例化,包括反射创建bean对象、属性填充、循环依赖的处理、bean的初始化等等;
- l、finishRefresh():容器刷新完成之后的一些处理工作;
重点关注上述的第五步:invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor的postProcessBeanFactory()增强方法;这一步会执行invokeBeanDefinitionRegistryPostProcessors()方法,然后执行BeanDefinitionRegistryPostProcessor后置处理器的postProcessBeanDefinitionRegistry()增强方法。
其中就包含了ConfigurationClassPostProcessor的处理,加载bean定义信息、ConfigurationClassParser解析自动配置相关的类。
调用栈如下:
org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232)
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275)
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95)
org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:691)
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:528)
BeanDefinitionRegistryPostProcessor主要完成对@Configuration注解的处理,类图如下:
我们查看ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法的源码:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
在processConfigBeanDefinitions(registry)中通过ConfigurationClassParser#parse()方法解析bean定义信息:
查看ConfigurationClassParser#parse()方法:
parse()方法主要包括两个步骤:
2-1)、ConfigurationClassParser#parse()
主要是调用ConfigurationClassParser#processConfigurationClass来完成解析配置类:
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
真正解析配置类是在ConfigurationClassParser#doProcessConfigurationClass()方法,这里涉及解析很多自动配置相关的注解,如@PropertySource、@ComponentScan、@Import、@ImportResource等。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
自动配置注解的@Import就是在这里解析的。查看ConfigurationClassParser#processImports(),看下具体是如何解析@Import注解的:
查看源码可知,如果@Import导入的是DeferredImportSelector类型,则会将selector先保存到deferredImportSelectorHandler中,并不会马上执行ImportSelector的selectImports()方法,等到所有的注解都解析完成后,再执行;而非DeferredImportSelector类型,则直接调用ImportSelector的selectImports()方法,然后注入对应的Bean对象;
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)方法解析完注解bean定义信息后,才会执行延迟ImportSelector的bean导入,即调用this.deferredImportSelectorHandler.process()。
这里也印证了AutoConfigurationImportSelector实现的DeferredImportSelector的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,包括对@ImportResource、@Bean这些注解的处理。
2-2)、this.deferredImportSelectorHandler.process()
回到org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)方法的最后一行:
this.deferredImportSelectorHandler.process();
public void process() {
// 获取到上一步暂存在deferredImportSelectors中的ImportSelector
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
// 创建延迟ImportSelector的分组处理器,内部是分组的
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 第一步:register()
deferredImports.forEach(handler::register);
// 第二步:processGroupImports()
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
下面详细分析两个重要步骤。
2-2-1)、第一步:register()
register()方法主要作用是注册分组,DeferredImportSelectorGroupingHandler根据Group来分组,用于区分不同的ImportSelector。
- 1、groupings: key:组类型(在这里 AutoConfigurationGroup) value:组;
- 2、configurationClasses:key:配置类的注解属性 value:配置类信息;
2-2-2)、第二步:processGroupImports()
看下grouping.getImports()。ConfigurationClassParser.DeferredImportSelectorGrouping#getImports():
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
getImports()处理包括两步:
2-2-2-1)、process()方法
AutoConfigurationImportSelector内部定义了AutoConfigurationGroup,它实现了DeferredImportSelector.Group接口,并重写了process()和selectImports()方法。
所以,这里就会执行的是重写后的AutoConfigurationImportSelector.AutoConfigurationGroup#process()方法:
// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类
@Override
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()));
// 自动装配的方法入口
// 调用getAutoConfigurationEntry()方法得到自动配置类并封装成autoConfigurationEntry对象
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
annotationMetadata);
// 将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 遍历所有自动配置类
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
// 将符合条件的自动配置类存入到map中:Map<String, AnnotationMetadata> entries
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
AutoConfigurationImportSelector#getAutoConfigurationEntry()方法,获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费。
// 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
// 再次判断是否开启自动配置功能,判断是否有配置spring.boot.enableautoconfiguration属性,默认为true。
if (!isEnabled(annotationMetadata)) {
// 未开启自动配置,直接返回{}
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration注解的 exclude 和 excludeName 属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取META-INF/spring.factories文件配置的所有自动配置类,实际上是通过SpringFactoriesLoader.loadFactoryNames()获取
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 删除重复的自动配置类,实际上就是将List包装成LinkedHashSet去重
configurations = removeDuplicates(configurations);
// 获取到需要排除自动装配的类、类名称,以及配置文件中(属性名:spring.autoconfigure.exclude)配置的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查configurations中哪些是需要排除的
checkExcludedClasses(configurations, exclusions);
// 真正从候选自动配置类集合中移除掉需要排除的那些配置类
configurations.removeAll(exclusions);
// 执行AutoConfigurationImportFilter过滤器match方法判断是否符合@ConditionalOnBean,@ConditionalOnClass或@ConditionalOnWebApplication,循环所有的候选的自动配置组件,判断有没有哪些组件不符合条件的,将它们排除掉
configurations = filter(configurations, autoConfigurationMetadata);
// 获取了符合条件的自动配置类后,触发AutoConfigurationImportEvent事件,目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
fireAutoConfigurationImportEvents(configurations, exclusions);
// 最终的返回的结果,封装成AutoConfigurationEntry
return new AutoConfigurationEntry(configurations, exclusions);
}
AutoConfigurationImportSelector#getCandidateConfigurations 获取候选的自动配置类。
实际上是通过类加载器加载"META-INF/spring.factories"文件中EnableAutoConfiguration对应的自动配置类全类名。
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());
// 获取经过滤后所有符合条件的自动配置类
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());
}
自动配置的类全类名拿到后,执行selectImports()。
2-2-2-2)、selectImports()方法
获取所有要排除的自动配置类,然后删除不符合条件的那些自动配置类,最后做了一下排序。
至此,getImports()方法执行结束,然后遍历所有获取到的自动配置类的全限定类名,挨个调用processImports()方法注入bean。
我们回到org.springframework.context.annotation.ConfigurationClassParser#processImports()方法:
调用addImportBeanDefinitionRegistrar()方法将bean定义注册到importBeanDefinitionRegistrars中,最后通过ImportBeanDefinitionRegistrar#registerBeanDefinitions()方法注册对应的bean。
我们看org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions的如下代码,parse()方法前面已经分析完成了,获取到了自动配置的bean的定义信息,接下来会执行:this.reader.loadBeanDefinitions(configClasses);加载bean定义信息,注册bean到spring容器中。
到这里,SpringBoot的自动配置原理就分析完了。