@Import注解详细分析

@Import注解详细分析

1.写在前面

其实@Import只是Spring框架的注解之一,但是作用一点都不小,尤其是在Springboot中,源码中很多地方都使用到,本来这篇应该放在spring源码深度分析中,无奈,那篇实在太长了,只能单独扯出一篇,真是越学习越感觉出了Spring框架的强大!好了,废话不扯,圆规正传。先搭建环境。

2.搭建环境

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

创建几个基本的bean

public class A {
    public void test(){
        System.out.println("I am A");
    }
}
public class B {
    public void test(){
        System.out.println("I am B");
    }
}
public class C {
    public void test(){
        System.out.println("I am C");
    }
}

新建一个空的启动配置类

@Configuration
public class AppConfig {
    public void test(){

    }
}

创建一个启动spring的类,执行main方法spring容器就启动了。

public class TestBean {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig bean = context.getBean(AppConfig.class);
        bean.test();
    }
}

3.案例演示

关于@Import注解存在三种应用方式,通过传入的不同类型的参数来区分,这几种参数类型分别是

  • 普通类
  • 实现ImportSelector接口的类
  • 实现ImportBeanDefinitionRegistrar接口的类

3.1 普通类参数方式

在AppConfig类添加@Import({A.class})注解,参数是A.class,然后测试A的实例是否被注册到容器中。AppConfig类修改为:

@Configuration
@Import({A.class})
public class AppConfig {

    @Autowired
    private A a;

    public void test() {
        a.test();
    }
}

启动TestBean的main方法,参看结果。

I am A

通过结果可以得出结论,A的实例对象a已经被注册到容器中。

3.2 实现ImportSelector接口的类参数方式

为了测试这种方式,需要创建一个实现ImportSelector接口的类,这个类的selectImports方法只是将B的全类名返回。

public class BImportSelector implements ImportSelector {

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
            "com.li.study.bean.B"
        } ;
    }
}

修改AppConfig类

@Configuration
@Import({A.class, BImportSelector.class})
public class AppConfig {

    @Autowired
    private A a;

    @Autowired
    private B b;
    
    public void test() {
        a.test();
        b.test();
    }
}

启动TestBean的main方法,参看结果。

I am A
I am B

通过结果可以得出结论,B的实例对象b已经被注册到容器中。

3.3 实现ImportBeanDefinitionRegistrar接口的类参数方式

创建一个实现ImportBeanDefinitionRegistrar接口的实现类,实现类的registerBeanDefinitions方法手动将C的实例注册到容器。

public class CImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition bd = new RootBeanDefinition(C.class);
        registry.registerBeanDefinition("c", bd);
    }
}

修改AppConfig类

@Configuration
@Import({A.class, BImportSelector.class, CImportBeanDefinitionRegistrar.class})
public class AppConfig {

    @Autowired
    private A a;

    @Autowired
    private B b;

    @Autowired
    private C c;

    public void test() {
        a.test();
        b.test();
        c.test();
    }
}

启动TestBean的main方法,参看结果。

I am A
I am B
I am C

通过结果可以得出结论,B的实例对象b已经被注册到容器中。

3.4 总结

  • @Import注解的参数为普通类时,Spring会将该类的实例注册到容器中,如果这个普通类上有类似@Configuration注解,注册到容器是也会被识别为配置类

  • @Import注解的参数为实现ImportSelector接口的类时,容器会调用接口的selectImports方法进行注册。

  • @Import注解的参数为实现ImportBeanDefinitionRegistrar接口的类时,容器会调用接口的registerBeanDefinitions方法进行注册。

这里说明一下,上面的案例仅仅演示将一个类的对象注册到容器中,其实实现这种操作在Spring中很简单,我们可以在类上添加@Component注解或者在配置类中返回该类对象的方法上添加@Bean注解实现相同的事情,这两种方式很常用,但是大家想过为什么会存在两种方式呢?

其实这两种方式有各自的应用场景的,考虑下这种情况,要注册对象的类在某个jar中定义,这时候就无法使用@Component注解,可以使用@Bean注解,在配置类中的方法中返回对象。

与注册普通对象相比,@Import注解侧重的是注册配置类(通常指添加@Configuration注解的类)到容器中,当然@Import也可以注册普通类的对象到容器中,像上面演示的案例那样。

这里@Import注解的三种方式也是应用在不同的场景的,与@Component和@Bean的区别类似。当你无法在类上添加@Configuration注解时,就不能用@Import注解的第一种方式,只能通过第二或第三种方式实现。虽然在新版本的Spring中通过@Import注解导入配置类时不需要在配置类上添加@Configuration注解也会默认当做配置类处理,但最初的Spring设计不是这样的。

这里主要说明普通类和配置类是有区别的,虽然你可以通过添加@Configuration注解将任意类添加到容器中,但不规范,就像在Spring MVC中你通常使用@Service注解将service层的bean添加到容器中,如果你非要使用@Repository注解其实也可以时下功能。

以上纯属个人理解应用Spring的框架心得体会,理解的不准确的欢迎大佬指点。

好了,说完了应用,我们准备说原理了,开始分析源码。

前方高能,非战斗人员请撤离。

4 源码分析

其实完全理解一个技术实现就是看源码,源码都看得懂,应用还是问题吗?

我们就从TestBean的main方法开始分析,也就是下面这行代码,这里面的内容太值得研究了。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

跟下去的第一步

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
   this();
   register(componentClasses);
   refresh();
}

这里说一下这三行代码,一个行调用无参构造器,第二行将传入的启动配置类注册到容器中,第三行刷新容器,这里我们主要关注的就是第一行,与本主题关系比较大的也是第一行,但是第三行代码时Spring框架的核心方法之一,我们也会在关键的地方提一下,详细的大家参考spring源码深度分析这里不再赘述了。

我们进入向下继续,没什么关键的代码或者与本主题不相关直接过,重要的地方详细解释。

public AnnotationConfigApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

我们进入第一行代码

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
   this(registry, getOrCreateEnvironment(registry));
}

继续

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   Assert.notNull(environment, "Environment must not be null");
   this.registry = registry;
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

看最后一行

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
   registerAnnotationConfigProcessors(registry, null);
}

继续

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {

   DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
   if (beanFactory != null) {
      if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
         beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
      }
      if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
         beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
      }
   }

   Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

   if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);//关注这行
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
   if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
   if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition();
      try {
         def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
               AnnotationConfigUtils.class.getClassLoader()));
      }
      catch (ClassNotFoundException ex) {
         throw new IllegalStateException(
               "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
      }
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
   }

   return beanDefs;
}

到了第一个比较关键的点了,这里是注册了几个BeanDefinition到容器中,容器会在刷新的时候实例化这些注册的类,这几类是Spring创世纪的几个类,在实例化容器的时候就已经注册了,只是这里并没有实例化。

我们这里关注的是ConfigurationClassPostProcessor类,首先这个类实现了BeanDefinitionRegistryPostProcessor接口,这个很关键,通过这个就能知道类里面的方法什么时候被容器执行。这涉及到Spring的另一个知识点,请移步BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor用法与原理,这里有详细的介绍。

接下来我们就来分析ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法,至于为什么分析这个方法,上面的链接有答案。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);

   processConfigBeanDefinitions(registry);
}

这个方法前面几行是一些判断,我们来看最后一行。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();  //获取所有的beanDefinition

   //遍历所有的beanDefinition,如果是配置类的就加入到configCandidates集合
   //beanDefinition已经存在CONFIGURATION_CLASS_ATTRIBUTE属性的话,不处理
   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
         if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
         }
      }
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
         configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
   }

   //没有配置类,直接返回
   if (configCandidates.isEmpty()) {
      return;
   }

   //排序
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });

   // Detect any custom bean name generation strategy supplied through the enclosing application context
   SingletonBeanRegistry sbr = null;
   if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
               AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
         if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
         }
      }
   }

   if (this.environment == null) {
      this.environment = new StandardEnvironment();
   }

   //解析每个@Configuration类
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
      parser.parse(candidates);  //解析
      parser.validate();  //验证

      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);

      // Read the model and create bean definitions based on its content
      if (this.reader == null) {
         this.reader = new ConfigurationClassBeanDefinitionReader(
               registry, this.sourceExtractor, this.resourceLoader, this.environment,
               this.importBeanNameGenerator, parser.getImportRegistry());
      }
      this.reader.loadBeanDefinitions(configClasses);  //注册BeanDefinition
      alreadyParsed.addAll(configClasses);

      candidates.clear();
      //如果当前注册BeanDefinition数量大于方法开始执行时的数量
      //表示通过在上面的代码执行中有新的BeanDefinition注册进来
      //然后把新注册进来的BeanDefinition再处理一次
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
         String[] newCandidateNames = registry.getBeanDefinitionNames();
         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
         Set<String> alreadyParsedClasses = new HashSet<>();
         for (ConfigurationClass configurationClass : alreadyParsed) {
            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
         }
         for (String candidateName : newCandidateNames) {
            if (!oldCandidateNames.contains(candidateName)) {
               BeanDefinition bd = registry.getBeanDefinition(candidateName);
               if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                     !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                  candidates.add(new BeanDefinitionHolder(bd, candidateName));
               }
            }
         }
         candidateNames = newCandidateNames;
      }
   }
   while (!candidates.isEmpty());

   // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
   if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
   }

   if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
      // for a shared cache since it'll be cleared by the ApplicationContext.
      ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
   }
}

这段代码很长,但是逻辑很清晰。

首先找到所有的已经注册beanDefinition,然后遍历所有的beanDefinition找出@Configuration注解标记的配置类,然后解析,验证配置类,最后将加载的beanDefinition注册到容器。

先看解析的逻辑。

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();  //处理实现DeferredImportSelector接口的对象
}

这个方法按不同的条件调用parse方法,最后都会调用processConfigurationClass方法。

这里还重点提一下,关于最有一行代码this.deferredImportSelectorHandler.process(); ,这行代码处理了实现DeferredImportSelector接口的导入配置类的逻辑,DeferredImportSelector接口是ImportSelector的一个子接口,他是ImportSelector的一个变种,它的一个主要的特性是,它是在处理完其他配置类导入逻辑后再执行的,他的一个主要作用就是配合@Conditional注解,可以根据容器中的其他bean的导入情况来决定是否导入当前的配置类或者导入什么类型的配置类。SpringBoot的自动配置就是通过这个机制来实现的。这个接口又添加了额外的属性,来帮助判断过滤导入逻辑。

后面的处理导入逻辑的代码中对于实现DeferredImportSelector接口的处理逻辑是不一样的,总的来说他是将需要导入的配置类先保存起来,等通过其他的方式导入的配置类处理完成后,在处理本身的导入逻辑,这里面主要是一个先后顺序问题。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }

   ConfigurationClass existingClass = this.configurationClasses.get(configClass);
   if (existingClass != null) {
      if (configClass.isImported()) {
         if (existingClass.isImported()) {
            existingClass.mergeImportedBy(configClass);
         }
         // Otherwise ignore new imported config class; existing non-imported class overrides it.
         return;
      }
      else {
         // Explicit bean definition found, probably replacing an import.
         // Let's remove the old one and go with the new one.
         this.configurationClasses.remove(configClass);
         this.knownSuperclasses.values().removeIf(configClass::equals);
      }
   }

   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass, filter);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
   }
   while (sourceClass != null);

   this.configurationClasses.put(configClass, configClass);
}

循环调用doProcessConfigurationClass方法来处理配置类,然后将处理后的所有配置类缓存在configurationClasses中

protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
   }

   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // Process any @ImportResource annotations
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

这个方法有比较详细的注释,分别处理@Component,@PropertySources,@ComponentScans,@Import,@ImportResource几个注解,我们关注处理@Import注解的方法processImports

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      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) {  //遍历所有添加@Import注解的类
            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);
               }
               if (selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
               }
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                  processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, 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 =
                     ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                           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), 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接口的配置类,调用接口的selectImports方法
  • 如果实现了ImportBeanDefinitionRegistrar接口的配置类,将实现接口的类保存到集合中,供后面调用
  • 如果没有实现上面两个接口的配置类,递归调用processConfigurationClass方法。

这里存在一个逻辑细节,实现ImportBeanDefinitionRegistrar接口的源类的处理逻辑和其他的是不一样的,没有实现ImportBeanDefinitionRegistrar接口的源类,在这里会递归调用processConfigurationClass方法,最终都会当做配置类,放入当配置类的集合中,所以后面注册配置类,处理逻辑也是不一样的。

还有一个值得注意的是,对于DeferredImportSelector类型的导入类,进行了特殊处理,但是不再执行ImportSelector接口的selectImports方法了,这里处理完成后,他会在所有的解析逻辑完成后,执行DeferredImportSelector接口的process方法。

代码执行到这里,只是完成了对@Import注解的解析,并没有真正将需要注册的beanDefinition注册到容器中,接下来我们就关注真正的注册逻辑。

我们回到processConfigBeanDefinitions方法的this.reader.loadBeanDefinitions(configClasses);这是真正注册的代码逻辑

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}

遍历配置类,调用注册beanDefinition逻辑

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;
   }

   if (configClass.isImported()) {
       //通过@Import注解导入的配置类注册到容器中
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   //通过执行ImportBeanDefinitionRegistrar接口方法实现注册
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

我们看registerBeanDefinitionForImportedConfigurationClass方法,这个方法就是将所有的配置类注册到容器中,对于通过@Import注解方式注入的配置类,没有实现ImportBeanDefinitionRegistrar接口的都会在这里注册。

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
   AnnotationMetadata metadata = configClass.getMetadata();
   AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

   ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
   configBeanDef.setScope(scopeMetadata.getScopeName());
   String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
   AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

   BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
   definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
   this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
   configClass.setBeanName(configBeanName);

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

真正注册是this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());

我们来看loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
   registrars.forEach((registrar, metadata) ->
         registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

只有一个逻辑,通过执行ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法注册到容器中。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值