目的
前面的文章@Bean源码解析有说到,将一个bean,转换成BeanDefinition,并存入BeanDefinitionMap有三种方式;本文,我们要说的是@Import注解这种方式;
@Import是用来引入bean的;和本文相关的,我们需要关注ImportSelector和ImportBeanDefinitionRegistrar
接口说明
ImportBeanDefinitionRegistrar该接口,定义了一个public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
该方法的实现类,可以完成将bean添加到beanDefinitionMap中;因为在该方法中,可以获取到BeanDefinitionRegistry,只是:如果我们程序员自己去提供一个该接口的实现类,需要我们自己在接口实现类中,声明一个beanDefinition对象;并给beanDefinition对象设置属性
也可以在该方法中,修改已经存在于BeanDefinitionMap中的beanDefinition对象;
ImportSelector
该接口定义的方法,返回的是一个数组集合,我们只需要在实现类中定义我们要注入的bean的全类名,并放到return的集合中,即可;springboot的自动注入就是利用这个原理的,
通过SpringFactoriesLoader.loadFactoryNames获取到所有jar包中定义的要自动注入的bean对应的全类名;然后,spring会对所有要注入的bean进行解析;
源码
我前面的博客,有说到过,所有将bean转换为BeanDefinition的操作,都是在
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
这个方法中完成的,我们本次要说的@Import注解的处理,也是在这个方法内部完成的;
我们直接来看processImports(configClass, sourceClass, getImports(sourceClass), true);这行代码完成的动作;因为这个方法是本篇文章所要关注的
解析Import注解
getImports(sourceClass):这个方法内部嵌套的比较简单,就是获取到当前配置类上所有的注解,然后获取到@Import注解对应的value,也即:import注入的类
我们接着来看org.springframework.context.annotation.ConfigurationClassParser#processImports这个方法的处理:
先说这个方法的整体处理逻辑:
Import注解引入的类,有三种情况
-
ImportSelector:
1.1 判断Import引入的ImportSelector的实现类是否是DeferredImportSelector的实现类;DeferredImportSelector这个接口,在springboot自动注入的时候有用到;可以看下图1 AutoConfigurationImportSelector的继承关系:1.2 如果是非DeferredImportSelector的实现类,就正常处理:ImportSelector的实现类中,return的全类名数组; 在这里, 会依次解析每个类,防止要注入的类中有ImportSelector和ImportBeanDefinitionRegistrar的实现类;
对于ImportSelector实现类,返回的普通bean存放到了configurationClasses中 会在org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(java.util.Set)方法中初始化,完成bean到BeanDefinition的转换 -
ImportBeanDefinitionRegistrar:
这里的处理比较简单;将实现类的registrar对象和metadata存入到importBeanDefinitionRegistrars这个map中,也是在org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(java.util.Set)进行beanDefinition对象的生成 -
普通类(bean):
存入到了configurationClasses这个集合中;如果ImportSelector实现类返回的bean就是一个普通bean,那就会在第一次递归调用processImports的时候,在第三步这里添加到集合中;否则,会一直递归调用,直到解析到import的是普通bean;
也即:对于ImportBeanDefinitionRegistrar的实现类,直接存入到了map集合中;对于ImportSelector的实现类,需要在解析return的所有bean,防止return的bean中还有ImportSelector或者ImportBeanDefinitionRegistrar的实现类
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));