在 SpringBoot 中, 我们不用配置bean.xml, 也不用配置 @ComponentScan注解 , 那为什么启动类同级包及子包中的组件类依然能被IOC创建并管理, 但除此之外的包的组件类就扫描不到呢?
点开此注解后, 我们看到
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication {}
原来
@SpringBootApplication == @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan(…)
@SpringBootConfiguration 是继承于 @Configuration 的, 二者功能一致
对于 @Configuration 的讲解请看下篇文章
@Configuration注解分析
作用相当于 Spring 中的 context:component-scan 标签, 用来扫描带有组件标识的类, 以便于注册到 IOC 中.
按照官方的说法, @ComponentScan 注解可不加任何参数, 不加参数默认扫描当前类同级包及子包组件类.
常用参数
1, basePackageClasses : 用来扫描带有组件标识的类(例子中的String, Integer无组件标识, 所以会抛出异常)
例: @ComponentScan(basePackageClasses = {Test1.class, Test2.class})
也可以扫描一个类 -> @ComponentScan(basePackageClasses = Test.class)
2, basePackages : 用来扫描包中带有组件标识的类
例: @ComponentScan(basePackages = {“com.baidu.pojo”, “com.ali.pojo”})
也可以扫描一个包 -> @ComponentScan(basePackages = “com.baidu.pojo”)
@EnableAutoConfiguration 注解是基于 @Import 将所有符合注册条件的 Bean 加载到 IOC 容器中
打开 @EnableAutoConfiguration 注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
@EnableAutoConfiguration == @AutoConfigurationPackage + @Import
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
@Import(AutoConfigurationPackages.Registrar.class) 给容器中导入 AutoConfigurationPackages 的静态内部类: Registrar
Registrar源码:
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
/**
* 根据传入的元注解信息获取所在的包, 将包中组件类封装为数组进行注册,
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
@AutoConfigurationPackage 默认将主配置类( @SpringBootApplication )所在的包及其子包里面的所有组件扫描到IOC容器中。
@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class) 给容器中导入 AutoConfigurationImportSelector 组件
AutoConfigurationImportSelector继承关系:
实现了一些 Spring 中 xxxAware, 引入了一些必要组件之外, 还实现了 ImportSelector 接口
AutoConfigurationImportSelector 接口何时被执行?
SpringBoot 启动时会使用 ConfigurationClassParser 来解析被 @Configuration 标识的配置类, 然后再处理这个类内部被其他注解修饰的情况, 比如 @Import 注解, @ComponentScan 注解,@Bean 注解等
如果发现注解中存在 @Import(ImportSelector) 的情况下,就会创建一个相应的 ImportSelector 对象,并调用其 process 方法
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()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
在 process 方法中, 实现真正逻辑的是在 第8行 调用的 getAutoConfigurationEntry 方法
getAutoConfigurationEntry:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取 xxxConfiguration 类
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);
}
利用 getAutoConfigurationEntry 向容器中导入组件
在 getAutoConfigurationEntry 方法中我们可以看到有个方法: getCandidateConfigurations(annotationMetadata, attributes)
那 getCandidateConfigurations(annotationMetadata, attributes) 是怎么获取到 127 个 Configuration 类的呢?
getCandidateConfigurations(annotationMetadata, attributes):
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
在此方法, 我们看到 List(String) configurations = SpringFactoriesLoader.loadFactoryNames(…);
可以推测, 是 SpringFactoriesLoader 帮我们加载了 Configuration
再来看看 SpringFactoriesLoader 中最关键的 loadSpringFactories 方法
进入 loadFactoryNames 方法,会发现 loadFactoryNames 读取了 ClassPath 下面的 META-INF/spring.factories 文件
这也就说 SpringBoot 一启动, 就要给容器加载的 127 个配置类, 对应上面断点看到的 127
问题来了!
可以看出, SpringBoot 启动一共获取到 129 个 Bean, 除去我们自己的 Bean, 还有必要组件外, 那也不够 127 个, 那剩余的 Configuration 被注册到哪里去了呢?
我们打开任意一个配置类, 以 AopConfiguration 为例:
原来, 并不是每一个 Configuration 类都会被注册的, 加了 @Conditional 以条件注册, 不满足, 则不注册