欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring注解@Import原理解析

 更新時(shí)間:2023年02月24日 14:32:59   作者:噗噗C  
這篇文章主要為大家介紹了Spring注解@Import原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

在項(xiàng)目開發(fā)的過程中,我們會(huì)遇到很多名字為 @Enablexxx 的注解,比如@EnableApolloConfig、 @EnableFeignClients、 @EnableAsync 等。他們的功能都是通過這樣的注解實(shí)現(xiàn)一個(gè)開關(guān),決定了是否開啟某個(gè)功能模塊的所有組件的自動(dòng)化配置,這極大的降低了我們的使用成本。

那么你是好奇過 @Enablexxx 是如何達(dá)到這種效果呢,其作用機(jī)制是怎么樣的呢?

@Import 原理

按照默認(rèn)的習(xí)慣,我們會(huì)把某個(gè)功能模塊的開啟注解定義為 @Enablexxx,功能的實(shí)現(xiàn)和名字格式其實(shí)無關(guān),而是其內(nèi)部實(shí)現(xiàn),這里用 @EnableAsync 來舉例子。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
 ……
}

可以看到除了3個(gè)通用注解,還有一個(gè)@Import(AsyncConfigurationSelector.class)注解,顯然它真正在這里發(fā)揮了關(guān)鍵作用,它可以往容器中注入一個(gè)配置類。

在 Spring 容器啟動(dòng)的過程中,執(zhí)行到調(diào)用invokeBeanFactoryPostProcessors(beanFactory)方法的時(shí)候,會(huì)調(diào)用所有已經(jīng)注冊(cè)的 BeanFactoryPostProcessor,然后會(huì)調(diào)用實(shí)現(xiàn) BeanDefinitionRegistryPostProcessor 接口的后置處理器 ConfigurationClassPostProcessor ,調(diào)用其 postProcessBeanDefinitionRegistry() 方法, 在這里會(huì)解析通過注解配置的類,然后調(diào)用 ConfigurationClassParser#doProcessConfigurationClass() 方法,最終會(huì)走到processImports()方法,對(duì) @Import 注解進(jìn)行處理,具體流程如下。

如果這部分流程不是很理解,推薦詳細(xì)閱讀一下 Spring 生命周期相關(guān)的代碼,不過不重要,不影響理解后面的內(nèi)容。

@Import 注解的功能是在ConfigurationClassParser類的 processImports()方法中實(shí)現(xiàn)的,對(duì)于這個(gè)方法我已經(jīng)做了詳細(xì)的注釋,請(qǐng)查看。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
   Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
   boolean checkForCircularImports) {

  // 如果使用@Import注解修飾的類集合為空,直接返回
  if (importCandidates.isEmpty()) {
   return;
  }
  // 通過一個(gè)棧結(jié)構(gòu)解決循環(huán)引入
  if (checkForCircularImports && isChainedImportOnStack(configClass)) {
   this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
  }
  else {
   // 添加到棧中,用于處理循環(huán)import的問題
   this.importStack.push(configClass);
   try {
    // 遍歷每一個(gè)@Import注解的類
    for (SourceClass candidate : importCandidates) {
     // 1. 
          // 檢驗(yàn)配置類Import引入的類是否是ImportSelector子類
     if (candidate.isAssignable(ImportSelector.class)) {
      // Candidate class is an ImportSelector -> delegate to it to determine imports
      // 候選類是一個(gè)導(dǎo)入選擇器->委托來確定是否進(jìn)行導(dǎo)入
      Class<?> candidateClass = candidate.loadClass();
      // 通過反射生成一個(gè)ImportSelect對(duì)象
      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);
      }
            
      // 判斷引用選擇器是否是DeferredImportSelector接口的實(shí)例
      // 如果是則應(yīng)用選擇器將會(huì)在所有的配置類都加載完畢后加載
      if (selector instanceof DeferredImportSelector) {
       // 將選擇器添加到deferredImportSelectorHandler實(shí)例中,預(yù)留到所有的配置類加載完成后統(tǒng)一處理自動(dòng)化配置類
       this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
      }
            
      else {
       // 獲取引入的類,然后使用遞歸方式將這些類中同樣添加了@Import注解引用的類
              // 執(zhí)行 ImportSelector.selectImports
       String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
       Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
       // 遞歸處理,被Import進(jìn)來的類也有可能@Import注解
       processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
      }
     }
          // 2.
     // 如果是實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口的bd
     else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
      // Candidate class is an ImportBeanDefinitionRegistrar ->
      // delegate to it to register additional bean definitions
      // 候選類是ImportBeanDefinitionRegistrar  -> 委托給當(dāng)前注冊(cè)器注冊(cè)其他bean
       Class<?> candidateClass = candidate.loadClass();
      ImportBeanDefinitionRegistrar registrar =
        ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
          this.environment, this.resourceLoader, this.registry);
      /**
       * 放到當(dāng)前configClass的importBeanDefinitionRegistrars中
       * 在ConfigurationClassPostProcessor處理configClass時(shí)會(huì)隨之一起處理
       */
      configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
     }
     else {
      // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
      // process it as an @Configuration class
      // 候選類既不是ImportSelector也不是ImportBeanDefinitionRegistrar-->將其作為@Configuration配置類處理
      this.importStack.registerImport(
        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
      /**
       * 如果Import的類型是普通類,則將其當(dāng)作帶有@Configuration的類一樣處理
       */
      processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
     }
    }
   }
   catch (BeanDefinitionStoreException ex) {
   ……
   finally {
    this.importStack.pop();
   }
  }
 }

上述代碼的核心邏輯無非就是如下幾個(gè)步驟。

  • 找到被 @Import 修飾的候選類集合,依次循環(huán)遍歷。
  • 如果該類實(shí)現(xiàn)了ImportSelector接口,就調(diào)用 ImportSelectorselectImports() 方法,這個(gè)方法返回的是一批配置類的全限定名,然后遞歸調(diào)用processImports()繼續(xù)解析這些配置類,比如可以 @Import 的類里面有 @Import 注解,在這里可以遞歸處理。
  • 如果被修飾的類沒有實(shí)現(xiàn) ImportSelector 接口,而是實(shí)現(xiàn)了ImportBeanDefinitionRegistrar 接口,則把對(duì)應(yīng)的實(shí)例放入importBeanDefinitionRegistrars 這個(gè)Map中,等到ConfigurationClassPostProcessor處理 configClass 的時(shí)候,會(huì)與其他配置類一同被調(diào)用 ImportBeanDefinitionRegistrarregisterBeanDefinitions() 方法,以實(shí)現(xiàn)往 Spring 容器中注入一些 BeanDefinition。
  • 如果以上的兩個(gè)接口都未實(shí)現(xiàn),則進(jìn)入 else 邏輯,將其作為普通的 @Configuration 配置類進(jìn)行解析。

所以到這里,你應(yīng)該明白 @Import 的作用機(jī)制了吧。對(duì)上述邏輯我總結(jié)了一張圖,如下。

示例 @EnableAsync

繼續(xù)之前提到的 @EnableAsync 作為例子,源碼如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
 Class<? extends Annotation> annotation() default Annotation.class;
 boolean proxyTargetClass() default false;
 AdviceMode mode() default AdviceMode.PROXY;
 int order() default Ordered.LOWEST_PRECEDENCE;
}
// 
@Override
 public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
  ……
    // 獲取 Mode
  AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
    // 模板方法,由子類去實(shí)現(xiàn)
  String[] imports = selectImports(adviceMode);
  if (imports == null) {
   throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
  }
  return imports;
 }

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

 @Override
 @Nullable
 public String[] selectImports(AdviceMode adviceMode) {
  switch (adviceMode) {
   case PROXY:
    return new String[] {ProxyAsyncConfiguration.class.getName()};
   case ASPECTJ:
    return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
   default:
    return null;
  }
 }

}

它通過 @Import 注解引入了AsyncConfigurationSelector配置類,它繼承了 AdviceModeImportSelector 類,而后者實(shí)現(xiàn)了 ImportSelector 接口,里面的實(shí)現(xiàn)了一個(gè)由注解指定 mode 屬性來決定返回的配置類的邏輯,而 mode 的默認(rèn)值就是 AdviceMode.PROXY。

對(duì)應(yīng) switch 邏輯,將返回 ProxyAsyncConfiguration類的全限定名。這就對(duì)應(yīng)了 @Import 處理邏輯的第一個(gè) if 邏輯塊,它將會(huì)解析這個(gè)類,然后遞歸調(diào)用processImports(),再次進(jìn)入此方法,進(jìn)入第三個(gè)else邏輯塊,將其當(dāng)作一個(gè)普通配置類解析??梢钥吹?ProxyAsyncConfiguration 其實(shí)就是 @Configuration 類,它的作用是注冊(cè)一個(gè) Bean 對(duì)象 AsyncAnnotationBeanPostProcessor。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
   @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
      ……
      return bpp;
   }
}

以上就是Spring注解@Import原理解析的詳細(xì)內(nèi)容,更多關(guān)于Spring注解@Import原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Springboot項(xiàng)目啟動(dòng)找不到啟動(dòng)類的解決

    Springboot項(xiàng)目啟動(dòng)找不到啟動(dòng)類的解決

    這篇文章主要介紹了Springboot項(xiàng)目啟動(dòng)找不到啟動(dòng)類的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 基于SpringBoot開機(jī)啟動(dòng)與@Order注解

    基于SpringBoot開機(jī)啟動(dòng)與@Order注解

    這篇文章主要介紹了SpringBoot開機(jī)啟動(dòng)與@Order注解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • springmvc @RequestBody String類型參數(shù)的使用

    springmvc @RequestBody String類型參數(shù)的使用

    這篇文章主要介紹了springmvc @RequestBody String類型參數(shù)的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • springboot項(xiàng)目整合注冊(cè)功能模塊開發(fā)實(shí)戰(zhàn)

    springboot項(xiàng)目整合注冊(cè)功能模塊開發(fā)實(shí)戰(zhàn)

    這篇文章主要介紹了springboot項(xiàng)目整合注冊(cè)功能模塊開發(fā)實(shí)戰(zhàn),在用戶的注冊(cè)是首先需要查詢當(dāng)前的用戶名是否存在,如果存在則不能進(jìn)行注冊(cè),相當(dāng)于一個(gè)查詢語(yǔ)句,本文通過實(shí)例代碼詳細(xì)講解,需要的朋友可以參考下
    2022-11-11
  • 一篇文章帶你了解mybatis的動(dòng)態(tài)SQL

    一篇文章帶你了解mybatis的動(dòng)態(tài)SQL

    這篇文章主要為大家介紹了mybatis的動(dòng)態(tài)SQL?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • SpringBoot集成Hadoop對(duì)HDFS的文件操作方法

    SpringBoot集成Hadoop對(duì)HDFS的文件操作方法

    這篇文章主要介紹了SpringBoot集成Hadoop對(duì)HDFS的文件操作方法,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • springcloud 熔斷器Hystrix的具體使用

    springcloud 熔斷器Hystrix的具體使用

    本篇文章主要介紹了springcloud 熔斷器Hystrix的具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • MyBatis關(guān)聯(lián)查詢的實(shí)現(xiàn)

    MyBatis關(guān)聯(lián)查詢的實(shí)現(xiàn)

    MyBatis可以通過定義多個(gè)表的關(guān)聯(lián)關(guān)系,實(shí)現(xiàn)多表查詢,本文主要介紹了MyBatis關(guān)聯(lián)查詢的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • SpringMVC 中文亂碼的解決方案

    SpringMVC 中文亂碼的解決方案

    這篇文章主要介紹了SpringMVC 中文亂碼的解決方案,幫助大家更好的理解和學(xué)習(xí)使用SpringMVC,感興趣的朋友可以了解下
    2021-04-04
  • java中關(guān)于轉(zhuǎn)義字符的一個(gè)bug

    java中關(guān)于轉(zhuǎn)義字符的一個(gè)bug

    本文主要介紹了java中關(guān)于轉(zhuǎn)義字符的一個(gè)bug。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02

最新評(píng)論