BeanFactory后置处理器 - ConfigurationClassPostProcessor - Import
开始阅读 Import 源码之前, 也是需要些一些测试demo, 来帮助理解和调试
demo
建几个内容一样的类, 如: IndexDao1, IndexDao2, IndexDao3
其具体内容如下:
public class IndexDao1 { public IndexDao1() { System.out.println("IndexDao1 -- constructor"); } private String name = "IndexDao1"; public String getName() { return name; } public void setName(String name) { this.name = name; } }
配置类:
@EnableIndexRegistrar @EnableIndex @Import(IndexDao3.class) public class StartConfig {}
注解类:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(IndexSelector.class) public @interface EnableIndex { boolean open() default true; }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(IndexRegistrar.class) public @interface EnableIndexRegistrar { boolean open() default true; }
selector:
public class IndexRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annAttr = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableIndexRegistrar.class.getName()) ); boolean open = (boolean) annAttr.get("open"); if(!open){ return; } BeanDefinition bd = new RootBeanDefinition(IndexDao2.class); registry.registerBeanDefinition("indexDao2", bd); } }
public class IndexSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { AnnotationAttributes annAttr = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableIndex.class.getName()) ); boolean open = (boolean) annAttr.get("open"); if(!open){ return new String[0]; } return new String[]{IndexDao1.class.getName()}; } }
测试方法:
public static void main(String[] args) { AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(StartConfig.class); IndexDao1 bean1 = acac.getBean(IndexDao1.class); IndexDao2 bean2 = acac.getBean(IndexDao2.class); IndexDao3 bean3 = acac.getBean(IndexDao3.class); }
结果:
这个demo中, 我使用了 selector, registrar, 普通类的方式, 分别让把 IndexDao1, IndexDao2, IndexDao3 交给spring了.
通过 import 这种方式, 可以控制加载一些组件, 如 mybatis, EnableCaching
源码
接着之前的 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
这个方法中调用了 processImports , 这个是用来处理 Import 的.
/** * 处理@Import imports 3种情况 * ImportSelector 将类的字符串数组返回给spring, 这些类的创建过程, 完全由 spring 去解析创建, 经典示例: @EnableCaching * 普通类 普通类会生成 db, 放到 Map<ConfigurationClass, ConfigurationClass> configurationClasses 中,
* 等待 parse 方法执行完后, 注册到 spring 容器中, 由 spring 去创建 * ImportBeanDefinitionRegistrar 用户创建(或扫描创建) bd, 然后将这些bd注册到容器中, 由spring去创建, 经典示例: mybatis */ 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); //判断是否是 延迟Import, 这个不用管 if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { //拿到 import 返回的类的字符串数组 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); //将字符串解析为 SourceClass, 里面通过 ClassUtils.forName 的方式, 加载类, 再封装为 SourceClass Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); //检测 返回的类中, 是否还有 @Import, 这里需要递归, 因为不能确定出来的类中, 是否还有@Import 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)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
先看一下, ImportSelector 和 ImportBeanDefinitionRegistrar 里面分别是什么
ImportSelector
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); }
ImportSelector 返回的, 其实是 类的名称的字符串数组.
ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar { void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
这个接口没有返回值, 但是提供了一个注册器. 我们可以在注册器中, 注册 bd
上面这个 processImports 方法中, 调用了 ImportSelector 的 selectImports 方法, 但是却没有调用 ImportBeanDefinitionRegistrar 的方法.
说明她的方法不是在这里调用的. 那么他是在哪里调用的呢?
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 中的
this.reader.loadBeanDefinitions(configClasses);
其调用栈:
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions
普通类:
普通类的时候, 不会调用什么接口, 但是会对这个普通类进行解析, 生成 bd