@AutoConfigurationPackage
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
@Import
为spring的注解,用来导入配置类或者一些需要前置加载的类,通过进一步点击查看源代码,可以看到Registrar.class
实现了ImportBeanDefinitionRegistrar
接口,通过@Import
导入该接口的实现类会执行重写的registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
方法。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
通过断点测试,我们可以发现(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])
的结果是当前启动类所在的包名。而AutoConfigurationPackages.register(BeanDefinitionRegistry registry, String... packageNames)
方法的作用是将启动类所在包名/路径名加入容器中,一个专门记录packageNames的集合里。容器会扫描该集合下的路径名,从而去导入相应的组件。
即,@AutoConfigurationPackage 就是将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描注冊到 spring 容器中。
@Import({AutoConfigurationImportSelector.class})
:
@Import
注解导入的该类,实现了DeferredImportSelector
接口,而DeferredImportSelector
又实现了ImportSelector
接口,因此该类实现了ImportSelector
接口。通过@Import
导入的该接口的实现类会执行重写的selectImports(AnnotationMetadata annotationMetadata)
方法,该方法的返回值是String[]
类型,即需要自动配置/导入Spring容器中的类的全限定名。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
NO_IMPORTS
是new String[0]
,因此我们关注else部分,而else部分只有两条语句,重点在第一条语句对于getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
的调用。
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
这里点进去getCandidateConfigurations(annotationMetadata, annotationAttributes)
方法,可以看到方法体中调用另外一个方法loadFactoryNames(factoryType, classLoader)
,其中factoryType
的值为EnableAutoConfiguration
,classLoader
的值为AppClassLoader
(打断点可以观察到)。
loadFactoryNames(factoryType, classLoader)
最终返回另外一个方法调用的筛选结果,即loadSpringFactories(classLoader)
,点进去该方法的源码,在该位置打断点进行观察:
可以看到返回值result的结果是一个key-value保存的Map,展开如下。其中就有我们需要并且由loadFactoryNames(factoryType, classLoader)
方法返回的EnableAutoConfiguration
部分。loadSpringFactories(classLoader)
遍历了所有jar包路径下的META-INF/spring.factories
文件,这些文件给Spring中的容器组件进行了相关的设置,并汇总成为一个Map集合,最后返回。
从内往外推的顺序如下:
-
loadSpringFactories()
方法加载所有jar包下的META-INF/spring.factories
文件中的配置,并将其整理为Map,传递出去;
-
loadFactoryNames()
接收到loadSpringFactories()
的调用结果,挑选出key为EnableAutoConfiguration
的部分,即需要自动配置的部分,将其返回;
-
getAutoConfigurationEntry()
获得META-INF/spring.factories
需要自动配置的部分,在根据@OnXXXCondition
注解筛选排除部分不生效的自动配置,将其返回给selectImports()
方法;
-
点进去Map中的EnableAutoConfiguration
,可以看到如下全为"xxxxAutoConfiguration"格式的字符串,这就是selectImports()
方法返回的需要添加到Spring容器中管理的组件的全限定类名,作用类似于使用@Import
引入了@Configuration
注解的类,通过这种方式实现自动配置。