Spring源码(十)configurationClassPostProcessor下的@Import注解解析
@Import
@import代码形式
对于import注解,用springBoot的SpringBootapplication的自动配置原理写一下,因为比较方便,有现成的@import。
一般我们回答springboot的自动装配原理一般会将注解,会说@SpringBootapplication里面有三个注解,最重要的是@EnableAutoConfiguration,而@EnableAutoConfiguration里面有@Import(AutoConfigurationImportSelector.class),找到AutoConfigurationImportSelector这个类的getCandidateConfigurations方法就能将Spring.factories下的enableautoConfiguration读进来。但是怎么找到getCandidateConfigurations方法呢?我们换一个角度去看这个问题。从configurationClassPostProcessor下看。
首先会进入到doProcessConfigurationClass处理的注解类下,有个方法处理@import的,processImports方法。
这里面的参数也比较重要,首先看下getImports参数方法。
这里定义了两个集合,然后调用了collectImports(sourceClass, imports, visited);方法。
这里的最下面的if判断,如果遍历的注释没有@import注解,那么就需要进入递归的逻辑里面去了,因为有可能在@a下的@b下才有@import,而这个时候只能找到@b,不能找到@import。在@springbootApplication里,实际上里面有三个注解,这个三个注解并没有@import,实际上在@enableautoConfiguration里面有@import注解,所以这里需要递归处理不带@import的注解。
这里实际上在getImports的imports集合返回了2个,在enableAutoConfiguration注解下有个autoConfigurationPackage注解,里面也有个@import,这里我们忽略掉。
然后执行processImports(configClass, sourceClass, getImports(sourceClass), true);方法。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
在processImports方法中,我们注意到实际上import有不同类型,这里需要区别对待,而在 @EnableAutoConfiguration下的@import的类为DeferredImportSelector,而DeferredImportSelector继承了ImportSelector。所以这里走延迟导入,实例化创建了对象,并加入到deferredImportSelectors集合中去。延迟处理是在@configuration完成之后再处理这个方法。
继续回到doProcessConfigurationClass方法中来,处理完毕processImports。由于我们采用了延迟处理的方案,所以需要全部走完这个方法,一直返回到parse还没有分annotation、abstractBeanDefinition时。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof 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);
}
}
this.deferredImportSelectorHandler.process();
}
看上面方法最后一行this.deferredImportSelectorHandler.process();这里就是对加入到延迟处理集合中的方法进行处理了。看下process方法。
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
进入到processGroupImports处理方法下,在进入getImports是核心处理方法。
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
进入到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()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
进入到getAutoConfigurationEntry方法中。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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 = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
注意,process方法与getAutoConfigurationEntry已经在AutoConfigurationImportSelector类了,在getAutoConfigurationEntry中有一个方法getCandidateConfigurations,这就是我们需要的配置。
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;
}
进入loadFactoryNames方法,
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
进入到loadSpringFactories方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
上面这个方法就能将所有的beanFactories文件都加载回来,包含enableautoConfiguration。然后返回到getOrDefault,这个时候完成对enableautoConfiguration的识别。回到 getAutoConfigurationEntry方法里,去除掉一些不用的,最后包含20多个。
好了,@import结束。
@import总结
在BFPP下的,configurationClassPostProcessor包含了@import注解的解析,从启动类里面一个个挨个查找,最后找到@import的tutoConfigurationImportSelector的类,在这个类进行解析的时候,有个延迟处理的属性,在延迟处理时,有个getImports方法,这个方法有个getAutoConfigurationEntry,entry对象会调用getCandidateConfigurations方法,这个方法就会把enableconfiguration整个都加载回来,完成自动装配的环节。