@Import注解的作用为解析指定的class类,如果是简单的java类就直接作为一个bean放入容器中,如果是@Configuration就正常当做配置类解析,
如果实现了ImportSelector接口,就会调用selectImports接口方法将返回的字符串数组对应的class加载进容器并递归解析;
如果实现了ImportBeanDefinitionRegistrar接口,就会调用registerBeanDefinitions接口方法,通过这种方式可以自由的注册Bean到IOC容器中,mybatis的MapperScan注解就是通过这种方式扩展的
org.mybatis.spring.annotation.MapperScannerRegistrar,关于mybatis扩展的详细内容单独写一篇博文
下面看下spring代码是怎么处理这段逻辑的,入口为下方这个BeanDefinitionRegistryPostProcessor:
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
BeanDefinitionRegistryPostProcessor的调用入口在org.springframework.context.support.AbstractApplicationContext#refresh这里,详细的可以参考spring启动过程那篇博文:
该后处理bean能处理哪些bean?org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate给出了答案
首先是要有Configuration注解,其次是看org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate
是的,就是处理有Configuration注解,并且至少有这四个注解中的一个Component、ComponentScan、Import、ImportResource
回到org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
解析的核心方法为org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
ComponentScan注解的处理:
其他注解的处理:
重点看到@Import注解的处理逻辑:
for (SourceClass candidate : importCandidates) {
// ImportSelector注解处理逻辑 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); }
// 实现DeferredImportSelector接口的selector暂存放到最后处理,因为实现该接口的类通常会带有 @Conditional注解,只有等到其他所有
// bean都加载完了才能确定是否要添加该bean,典型的就是springboot的自动装配selector:org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
// 典型的注解如ConditionalOnMissingBean
if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else {
// 普通的ImportSelector就直接调用selectImports返回字符串数组,并递归解析这些class String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } }
// 实现ImportBeanDefinitionRegistrar接口的selector会先实例化并把实例放入到configClass的importBeanDefinitionRegistrars中暂存
// parse完回到后处理bean中,下方的ConfigurationClassBeanDefinitionReader会丛中注册beanDefinition
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 = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else {
// 这里的英文注释很清楚了吧,都不是上面两种情况的话直接当做@Configuration处理 // 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), exclusionFilter); } }
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass最后会把当前configClass放入集合
parse完回到后处理bean:ConfigurationClassPostProcessor
下面看下reader方法:
核心方法org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
至此,@Import方式扩展注册beanDefinition就基本都清晰了。