@Import注解源码分析

Bean的生产,首先是为bean定义出一个beanDefinition(bean定义),再根据beanDefinition创建bean, beanDefinition存入beanDifinitionMap之前会创建ConfigurationClass类,spring根据ConfigurationClass的信息,将beanDefinition存储map中,beanDefinition包括很多的信息,例如是否为单例,多例,是否为懒加载,DependsOn(创建bean之前需要创建的bean),是否@Primary,autowireMode,beanClass,initMethodName等等的信息.所有的bean定义都会存放进bean工厂的beanDefinitionMap中,后续会遍历map中的beanDefinition,并创建bean,如果beanDefinitionMap中不存在则不会创建.@Import的作用和@Component的作用有一些相似,都会创建bean.了解这些可以帮助我们分析@Import注解,本文也会结合源码分析@Import注解
Import只有一个变量 value

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

   /**
    * {@link Configuration @Configuration}, {@link ImportSelector},
    * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
    */
   Class<?>[] value();

}

@Import首先是在
ConfigurationClassParser#doProcessConfigurationClass的processImports(configClass, sourceClass, getImports(sourceClass), filter, true);被解析,此方法只是把类的信息封装进ConfigruationClass中

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 {
         //获取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();
               //实例化实现SelectImport接口的类
               ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
               
               ParserStrategyUtils.invokeAwareMethods(
                     selector, this.environment, this.resourceLoader, this.registry);
                              if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectors.add(
                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
               }
               else {
                  //调用ImportSelector的selectImports方法,返回String数组,类的全类名称
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  //String[]返回的类可能实现了ImportSelector或者ImportBeanDefinitionRegistrar接口,也有可能是
                  //一个普通的类,这里递归解析,直到为一个普通的类,或者说扫面到不实现ImportSelector接口的类
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                  processImports(configClass, currentSourceClass, importSourceClasses, false);
               }
            }
            //判断类是否实现了ImportBeanDefinitionRegistrar接口,不调用接口的实现方法,而是存入进configClass中
            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中,后续会被调用
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
//如果没有实现上面两个接口, candidate.asConfigClass(configClass)标注importedBy,后续判断是否是通过@Import导入的
//继续执行processConfigurationClass方法,可以点进去看一下,这里不扩展了, processConfigurationClass的作用就是
//类是否标注了@Component@ PropertySources@ ComponentScans@ ImportResource@Import这些注解,并根据相应的注解解析类
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
            }
         }
      }
      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();
      }
   }
}

通过上面的源码分析,分析@Import导入的类是否实现了ImportSelector和BeanDefinitionRegistrar接口,如果同时实现只解析ImportSelector

上面只是将一些信息存放到configclass中,代码执行到ConfigurationClassPostProcessor # processConfigBeanDefinitions 方法的this.reader.loadBeanDefinitions(configClasses);这段代码是开始解析configClasses,通过for循环遍历所有的configClasses;目的是向beanDefinitionMap注册beanDefinition

ConfigurationClassBeanDefinitionReader# loadBeanDefinitionsForConfigurationClass方法

private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

   if (trackedConditionEvaluator.shouldSkip(configClass)) {
      String beanName = configClass.getBeanName();
      if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
         this.registry.removeBeanDefinition(beanName);
      }
      this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
      return;
   }
//这里就是解析@Import类,上面提到过importedBy,这里就会解析
   if (configClass.isImported()) {
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//这里解析继承ImportBeanDefinitionRegistrars类,调用registerBeanDefinitions方法,其中两个入参AnnotationMetadata importingClassMetadata和 BeanDefinitionRegistry registry ,importingClassMetadata为原类信息,注意不是ImportBeanDefinitionRegistrars的实现类,是标注@Import类的元信息 registry可以手动的向beanDefinitionMap中注册beanDefinition

   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

registerBeanDefinitionForImportedConfigurationClass方法

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
   //获取配置类的元信息
   AnnotationMetadata metadata = configClass.getMetadata();
   //构建beanDefinition
   AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
   //设置scope
   ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
   configBeanDef.setScope(scopeMetadata.getScopeName());
   //获取bean的名称
   String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
   //处理我们的JRS250组件的
   AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

   BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
   definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
   //注册我们的bean定义到我们的容器中
   this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
   configClass.setBeanName(configBeanName);

   if (logger.isDebugEnabled()) {
      logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
   }
}

经过上面的分析,大致的了解@Import的机制,下面对分析@Import的使用

@Import(A.class)

  1. A没有实现ImportBeanDefinitionRegistrar和ImportSelector两个接口

A将会存放进IOC容器中

  1. A实现ImportSelector

A类不会存放进IOC容器中,调用selectImprots方法,Run存放进IOC容器中

public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[]{Run.class.getName ()};
}

  1. A类实现ImportBeanDefinitionRegistrar接口
    A类依旧不会存入IOC容器中,在调用registerBeanDefinitions方法的时候将Walk存放进IOC容器中,并设置beanName为walk
    当然这里可以操作beanDefinition我们就可以做很多的事,设置bean单例还是多例,是否为懒加载,dependsOn等很多的信息.
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    GenericBeanDefinition beanDefinition=new GenericBeanDefinition ();
    beanDefinition.setBeanClass(Walk.class);
    registry.registerBeanDefinition("walk",beanDefinition);
}

  1. @Import 配置 ImportAware使用

可以看我分享的文章 https://www.jianshu.com/p/8378b9f491ae

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容