在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    ImportSelector 與 DeferredImportSelector 的區(qū)別(spring4)

    ImportSelector 與 DeferredImportSelector 的區(qū)別(spring4)

    歡迎訪問我的 GitHub

    這里分類和匯總了欣宸的全部原創(chuàng)(含配套源碼): https://github.com/zq2599/blog_demos

    • 在使用 @Import 注解來注冊 bean 的時候,Import 注解的值可以是 ImportSelector 或者 DeferredImportSelector 的實現(xiàn)類,spring 容器會實例化這個實現(xiàn)類,并執(zhí)行其 selectImports 方法,那么問題來了: ImportSelector 和 DeferredImportSelector 的區(qū)別在哪里,我們自定義 Imort 邏輯的時候該選擇哪個呢? 本文通過分析相關的 spring 源碼來查找答案;

    全文概覽

    • 本文由以下幾部分組成:
  • 看官方文檔;
  • 分析 spring 源碼中對這兩個接口的處理;
  • 實戰(zhàn)驗證;
  • 看官方文檔

    • 先看官方文檔看起,我選擇了 4.3.9 版本在線文檔(這是個 Release 版),地址:https://docs.spring.io/spring/docs/4.3.19.RELEASE/javadoc-api/
    • 原文:A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.Implementations can also extend the Ordered interface or use the Order annotation to indicate a precedence against other DeferredImportSelectors.
    • 我的理解:
  • DeferredImportSelector 是 ImportSelector 的一個擴展;
  • ImportSelector 實例的 selectImports 方法的執(zhí)行時機,是在 @Configguration 注解中的其他邏輯被處理 之前 ,所謂的其他邏輯,包括對 @ImportResource、@Bean 這些注解的處理(注意,這里只是對 @Bean 修飾的方法的處理,并不是立即調(diào)用 @Bean 修飾的方法,這個區(qū)別很重要!);
  • DeferredImportSelector 實例的 selectImports 方法的執(zhí)行時機,是在 @Configguration 注解中的其他邏輯被處理 完畢之后 ,所謂的其他邏輯,包括對 @ImportResource、@Bean 這些注解的處理;
  • DeferredImportSelector 的實現(xiàn)類可以用 Order 注解,或者實現(xiàn) Ordered 接口來對 selectImports 的執(zhí)行順序排序;
  • 分析 spring 源碼中對這兩個接口的處理

    • 接下來看看源碼:
    • 在 spring-framework-4.1.8.RELEASE 工程中找到類 ConfigurationClassParser.java,這里面有處理配置類的主要邏輯;
    • 找到方法 parse(Set configCandidates):

    public void parse(Set configCandidates) { this.deferredImportSelectors = new LinkedList(); //檢查每個bean的定義 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { //對于每個有注解的類,都執(zhí)行方法parse(AnnotationMetadata metadata, String beanName) 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 (Exception ex) { throw new BeanDefinitionStoreException( “Failed to parse configuration class [” + bd.getBeanClassName() + “]”, ex); } } //最后再處理DeferredImportSelector的實現(xiàn)類 processDeferredImportSelectors(); }

    復制代碼

    • 由以上代碼可以大致看出 DeferredImportSelector 的實現(xiàn)類被最后放在 processDeferredImportSelectors 方法中處理,那么前面的 parse(AnnotationMetadata metadata, String beanName)做了些什么呢?繼續(xù)看;
    • 展開方法 parse(AnnotationMetadata metadata, String beanName)里面,是執(zhí)行 processConfigurationClass 方法;
    • 再展開 processConfigurationClass 方法,看到核心邏輯是調(diào)用 doProcessConfigurationClass 方法,展開看看:

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { //為了聚焦Import相關處理,此處略去部分不相關代碼,不在這里展示了 … … // 處理@Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // 處理@ImportResource注解 if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); String[] resources = importResource.getStringArray(“value”); Class readerClass = importResource.getClass(“reader”); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // 處理@Bean注解,注意是處理注解,不是執(zhí)行@Bean修飾的方法 Set beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 處理Configuration類的父類,外面在調(diào)用doProcessConfigurationClass方法的時有迭代處理,確保所有父類的注解都會被處理 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (!superclass.startsWith(“java”) && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // 再也沒有父類了,返回null表示當前Configuration處理完畢 return null; }

    復制代碼

    • 根據(jù)上述代碼分析,可以梳理出下圖中的邏輯:
    • 現(xiàn)在需要再看看 processImports 和 processDeferredImportSelectors 這兩個方法的具體代碼;
    • 先看 processImports 方法:

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, boolean checkForCircularImports) throws IOException { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && this.importStack.contains(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { //如果是ImportSelector接口的實現(xiàn)類,就在此處理 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class candidateClass = candidate.loadClass(); //實例化這些ImportSelector的實現(xiàn)類 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); //如果這實現(xiàn)類還實現(xiàn)了BeanFactoryAware、EnvironmentAware這些接口,就要先執(zhí)行這些接口中聲明的方法 invokeAwareMethods(selector); //如果這個實現(xiàn)類也實現(xiàn)了DeferredImportSelector接口,就被加入到集合deferredImportSelectors中 if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { //注意,這一行是關鍵代碼?。?!執(zhí)行實現(xiàn)類的selectImports方法 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } //此處略去的和ImportSelector不相關的邏輯代碼 … … … } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Exception ex) { throw new BeanDefinitionStoreException(“Failed to process import candidates for configuration class [” + configClass.getMetadata().getClassName() + “]”, ex); } finally { this.importStack.pop(); } } }

    復制代碼

    • 以上代碼有兩個關鍵點:
    • 第一、當前被處理的類,如果實現(xiàn)了 DeferredImportSelector 接口,就被加入到集合 deferredImportSelectors 中;
    • 第二、當前被處理的類,如果沒有實現(xiàn) DeferredImportSelector 接口,但是實現(xiàn)了 ImportSelector 接口,就被執(zhí)行 selectImports 方法;
    • 接下來看看 processDeferredImportSelectors 方法的源碼,提前推測應該是處理集合 deferredImportSelectors 中的所有類,這些類都實現(xiàn)了 DeferredImportSelector 接口:

    private void processDeferredImportSelectors() { List deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; //按照Order注解或者Ordered接口進行排序 Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR); for (DeferredImportSelectorHolder deferredImport : deferredImports) { ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { //此處是關鍵代碼,執(zhí)行DeferredImportSelector實現(xiàn)類的selectImports方法 String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata()); processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Exception ex) { throw new BeanDefinitionStoreException(“Failed to process import candidates for configuration class [” + configClass.getMetadata().getClassName() + “]”, ex); } } }

    復制代碼

    • 至此,源碼分析完畢了,從代碼可以很清晰的看出 ImportSelector 與 DeferredImportSelector 的區(qū)別,就是 selectImports 方法執(zhí)行時機有差別,這個差別期間,spring 容器對此 Configguration 類做了些其他的邏輯:包括對 @ImportResource、@Bean 這些注解的處理(注意,這里只是對 @Bean 修飾的方法的處理,并不是立即調(diào)用 @Bean 修飾的方法,這個區(qū)別很重要?。?;

    實戰(zhàn)驗證

    • 接下來到了實戰(zhàn)驗證的環(huán)節(jié)了,本次實戰(zhàn)的內(nèi)容是創(chuàng)建一個 springboot 工程,在里面自定義三個 ImportSelector 接口的實現(xiàn)類,如果您不想敲代碼,也可以去 github 下載源碼,地址和鏈接信息如下表所示:
    • 這個 git 項目中有多個文件夾,本章源碼在文件夾 customizeimportselector 下,如下圖紅框所示:
    • 開始編碼吧:
    • 我們創(chuàng)建三個 ImportSelector 的實現(xiàn)類來檢查其先后順序,三個 Selector 類簡介如下表,有兩個是 DeferredImportSelector 的實現(xiàn)類,一個是 ImportSelector 的實現(xiàn)類,每個 Selector 負責向 spring 容器注冊一種實例:
    • 基于 maven 創(chuàng)建 springboot 框架的 web 工程,pom.xml 內(nèi)容如下:

    4.0.0 com.bolingcavalry customizeimportselector 0.0.1-SNAPSHOT jar customizeimportselector Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin

    復制代碼

    • 創(chuàng)建三個接口 CustomizeService1、CustomizeService2、CustomizeService3,第一個源碼如下,另外兩個除了類名,其余部分一樣:

    package com.bolingcavalry.customizeimportselector.service;public interface CustomizeService1 { void execute();}

    復制代碼

    • 創(chuàng)建三個類,分別實現(xiàn)上面的三個接口,也是除了類名其余部分一樣:

    package com.bolingcavalry.customizeimportselector.service.impl;import com.bolingcavalry.customizeimportselector.service.CustomizeService1;public class CustomizeServiceImpl1 implements CustomizeService1 { public CustomizeServiceImpl1() { System.out.println(“construct : ” + this.getClass().getSimpleName()); } @Override public void execute() { System.out.println(“execute : ” + this.getClass().getSimpleName()); }}

    復制代碼

    • 創(chuàng)建 CustomizeImportSelector1:

    package com.bolingcavalry.customizeimportselector.selector;import org.springframework.context.annotation.DeferredImportSelector;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotationMetadata;@Order(102)public class CustomizeImportSelector1 implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { System.out.println(“selectImports : ” + this.getClass().getSimpleName()); return new String[]{“com.bolingcavalry.customizeimportselector.service.impl.CustomizeServiceImpl1”}; }}

    復制代碼

    • 創(chuàng)建 CustomizeImportSelector2:

    package com.bolingcavalry.customizeimportselector.selector;import org.springframework.context.annotation.DeferredImportSelector;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotationMetadata;@Order(101)public class CustomizeImportSelector2 implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { System.out.println(“selectImports : ” + this.getClass().getSimpleName()); return new String[]{“com.bolingcavalry.customizeimportselector.service.impl.CustomizeServiceImpl2”}; }}

    復制代碼

    • 創(chuàng)建 CustomizeImportSelector3,實現(xiàn)的是 ImportSelector 接口:

    package com.bolingcavalry.customizeimportselector.selector;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotationMetadata;public class CustomizeImportSelector3 implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { System.out.println(“selectImports : ” + this.getClass().getSimpleName()); return new String[]{“com.bolingcavalry.customizeimportselector.service.impl.CustomizeServiceImpl3”}; }}

    復制代碼

    • 創(chuàng)建配置類,將 CustomizeImportSelector1、CustomizeImportSelector2、CustomizeImportSelector3 全部用 Import 注解引入:

    package com.bolingcavalry.customizeimportselector;import com.bolingcavalry.customizeimportselector.selector.CustomizeImportSelector1;import com.bolingcavalry.customizeimportselector.selector.CustomizeImportSelector2;import com.bolingcavalry.customizeimportselector.selector.CustomizeImportSelector3;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration@Import({CustomizeImportSelector1.class, CustomizeImportSelector2.class, CustomizeImportSelector3.class})public class SysConfig {}

    復制代碼

    • 創(chuàng)建啟動類 CustomizeimportselectorApplication.java:

    package com.bolingcavalry.customizeimportselector;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class CustomizeimportselectorApplication { public static void main(String[] args) { SpringApplication.run(CustomizeimportselectorApplication.class, args); }}

    復制代碼

    • 啟動應用,可見輸入信息如下:

    2018-09-09 15:43:45.790 INFO 15364 — [ main] c.b.c.CustomizeimportselectorApplication : Starting CustomizeimportselectorApplication on DESKTOP-82CCEBN with PID 15364 (D:githubblog_demoscustomizeimportselectorargetclasses started by 12167 in D:githubblog_demoscustomizeimportselector)2018-09-09 15:43:45.791 INFO 15364 — [ main] c.b.c.CustomizeimportselectorApplication : No active profile set, falling back to default profiles: default2018-09-09 15:43:45.825 INFO 15364 — [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@641147d0: startup date [Sun Sep 09 15:43:45 GMT+08:00 2018]; root of context hierarchyselectImports : CustomizeImportSelector3selectImports : CustomizeImportSelector2selectImports : CustomizeImportSelector12018-09-09 15:43:46.425 INFO 15364 — [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)2018-09-09 15:43:46.430 INFO 15364 — [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2018-09-09 15:43:46.431 INFO 15364 — [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.232018-09-09 15:43:46.493 INFO 15364 — [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2018-09-09 15:43:46.493 INFO 15364 — [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 670 ms2018-09-09 15:43:46.569 INFO 15364 — [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: ‘dispatcherServlet’ to [/]2018-09-09 15:43:46.572 INFO 15364 — [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: ‘characterEncodingFilter’ to: [/*]2018-09-09 15:43:46.572 INFO 15364 — [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: ‘hiddenHttpMethodFilter’ to: [/*]2018-09-09 15:43:46.572 INFO 15364 — [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: ‘httpPutFormContentFilter’ to: [/*]2018-09-09 15:43:46.572 INFO 15364 — [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: ‘requestContextFilter’ to: [/*]construct : CustomizeServiceImpl1construct : CustomizeServiceImpl2construct : CustomizeServiceImpl3

    復制代碼

    • 從上述信息可以看出:
    • 首先、三個 selector 實現(xiàn)類的 selectImports 方法執(zhí)行順序符合預期:先執(zhí)行 ImportSelector 實現(xiàn)類的,再執(zhí)行 DeferredImportSelector 實現(xiàn)類的,并且 DeferredImportSelector 實現(xiàn)類的執(zhí)行順序會按照 Order 的設置 從小到大 執(zhí)行;
    • 其次、CustomizeServiceImpl1、CustomizeServiceImpl2、CustomizeServiceImpl3 的實例化順序并未受到影響;
    • 至此,ImportSelector 與 DeferredImportSelector 的區(qū)別已經(jīng)分析和驗證完畢,隨著對 Configuration 初始化處理邏輯的深入了解,我們可以定制出更靈活強大的配置邏輯,以符合業(yè)務需求;

    歡迎關注 InfoQ:程序員欣宸

    學習路上,你不孤單,欣宸原創(chuàng)一路相伴…

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶投稿
    上一篇 2022年6月28日 06:05
    下一篇 2022年6月28日 06:06

    相關推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息