1. 程式人生 > >spring [email protected]註解程式設計模型分析

spring [email protected]註解程式設計模型分析

@EnableXXXX程式設計模型

在spring boot中,@EnableXXX註解的功能通常是開啟某一種功能。根據某些外部配置自動裝配一些bean,來達到開啟某些功能的目的。光說很抽象,要具體分析。

@Enable模型的實現方式基本有3種。一個基本的@Enable註解的模型如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(XXXX.class)
public @interface EnableDiscoveryClient {

   /**
    * If true, the ServiceRegistry will automatically register the local server.
    */
boolean autoRegister() default true; }

對應XXXX.class的不同,有3種實現方式。

  • 普通配置類,裡面包含@Bean方法用於例項化bean
  • ImportSelector實現類
  • ImportBeanDefinitionRegistrar實現類

上面3種類都屬於@Import註解的匯入物件,整個外部化配置過程圍繞@Import註解進行解析,匯入類。

@Import註解處理時機節點(@Confguration註解的類處理)

@Import註解的處理過程大致可以描述為:

  1. 尋找BeanFactory中所有被@Configuration註解修飾的類,包括被@Configuration派生註解修飾的類。

  2. 尋找被@Configuration註解修飾的類上的所有註解元資訊(這裡的搜尋不僅是搜尋當前註解,還會迭代往修飾註解的註解的註解上層…..一直搜尋@Import,直到註解最原始的註解),獲取@Import註解的匯入類資訊,如果沒有則不處理。

  3. 根據匯入類的資訊,判定為

    • 普通配置類,裡面包含@Bean方法用於例項化bean
    • ImportSelector實現類
    • ImportBeanDefinitionRegistrar實現類

    3種形式進行處理。

從context啟動開始跟蹤主線處理程式碼,呼叫鏈條如下。

  • org.springframework.context.support.AbstractApplicationContext#refresh

  • org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

  • org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

  • org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(主線程式碼,必看)

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
      //定義@Conguration註解修飾的類註冊資訊列表
     List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
     String[] candidateNames = registry.getBeanDefinitionNames();
    //檢查當前context中所有的bean註冊資訊
     for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
              ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
           if (logger.isDebugEnabled()) {
              logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
           }
        }
         //檢查class是否是@Conguration註解修飾的類,包括被“繼承”@Conguration註解的註解,例如@SpringBootConguration,具體可以跟蹤ConfigurationClassUtils.checkConfigurationClassCandidate實現
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
           configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
     }
    
     // Return immediately if no @Configuration classes were found
     if (configCandidates.isEmpty()) {
        return;
     }
    
     // Sort by previously determined @Order value, if applicable
      //對配置類排序,順序由Ordered介面決定
     configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
     });
    
    //......略略略
    
     // Parse each @Configuration class
      //處理每一個配置類
     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);
    
         //......略略略
     }
     while (!candidates.isEmpty());
    //......略略略
    }
  • org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set

ImportSelector

String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());

返回結果是所有需要匯入的的類的全限定名。

對於全限定名陣列,逐個進行org.springframework.context.annotation.ConfigurationClassParser#processImports,相當於迴圈呼叫processImports,把新匯入的類也當做@Import匯入的類處理,如果新匯入的類繼續匯入新的類,就繼續org.springframework.context.annotation.ConfigurationClassParser#processImports。直到新匯入的類不是

ImportSelector

ImportBeanDefinitionRegistrar處理

當@Import的類是不是ImportSelector之後,如果是ImportBeanDefinitionRegistrar,那就做BeanDefinition資訊註冊到BeanFactory操作,具體實現在org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions實現,在這裡的處理過程是。

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);
         //將ImportBeanDefinitionRegistrar放入map快取起來
   configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
   this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

先快取@Import匯入的ImportBeanDefinitionRegistrar資訊,稍後統一呼叫ImportBeanDefinitionRegistrar載入註冊BeanDefinition資訊。

@Configurtion註解的類處理

重複上面的整個流程,處理這個被@Configuration註解標註的類。比較需要注意的是一般@Configuration註解標註的類常用@Bean方式來例項化例項。這裡#Bean也會解析出一個BeanMethod資訊集合,稍後跟ImportBeanDefinitionRegistrar的快取資訊一樣統一呼叫然後註冊BeanDefinition。

// Process individual @Bean methods
//對配置類的@Bean方法處理邏輯
//獲取所有@Bean標註的方法元資訊,後續處理
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

統一呼叫配置類解析出來的資訊註冊BeanDefinition

在解析完配置類之後,實際還沒有進行BeanDefinition的註冊,只得到了可以用來註冊BeanDefinition的“資訊工具”,利用@Bean得到了BeanMethod,@Import(xxxImportBeanDefinitionRegistrar.class)得到了ImportBeanDefinitionRegistrar的實現類。最終要使用這些工具進行BeanDefinition 資訊註冊。

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions中,當處理完@Configuration註解的類之後就進行ImportBeanDefinitionRegistrar的BeanDefinition註冊載入。

//處理@Configuration,遞迴尋找@Configuration,以及解析@Configuration裡面的@Import、@Bean、@Component、@ImportResource等。
parser.parse(candidates);
parser.validate();

//獲取parser中解析得到的所有配置類
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());
}
//根據遞迴找出的配置類和解析配置類得到的資訊,載入BeanDefinition
this.reader.loadBeanDefinitions(configClasses);
  • org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

    • org.springframework.context.annotation.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;
       }
    
       if (configClass.isImported()) {
          registerBeanDefinitionForImportedConfigurationClass(configClass);
       }
       for (BeanMethod beanMethod : configClass.getBeanMethods()) {
           //利用@Bean的Method載入BeanDefinition
          loadBeanDefinitionsForBeanMethod(beanMethod);
       }
       loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        //利用快取的ImportBeanDefinitionRegistrar載入註冊beandefintion
       loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }
    • org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars(以ImportBeanDefinitionRegistrar為例跟蹤)
      • org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(註冊BeanDefinition資訊到BeanFactory)