@Import底層實現原理


日常項目中,使用注解@EnableAspectJAutoProxy @EnableAsync

這里面涉及對@Import注解支撐的底層原理:ConfigurationClassPostProcessor 這個類,說到這個類,我們要先從SpringBoot啟動流程說起。

首先,看springboot啟動流程中的一步:

SpringApplication對象的run方法,創建上下文context = createApplicationContext(); 這一步,會創建AnnotationConfigServletWebServerApplicationContext對象:

contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

 類圖關系如下:

 AnnotationConfigServletWebServerApplicationContext類有兩個重要的屬性:private final AnnotatedBeanDefinitionReader reader;  private final ClassPathBeanDefinitionScanner scanner;

創建AnnotationConfigServletWebServerApplicationContext對象時, 調用構造方法,會初始化該對象的上面兩個屬性。見:

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

new AnnotatedBeanDefinitionReader(this)對象時,里面會涉及AnnotatedBeanDefinitionReader構造函數AnnotatedBeanDefinitionReader構造函數AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);其中會對spring內置管理的幾個特殊類封裝成BeanDefinition,緩存到map中;其中就包含對ConfigurationClassPostProcessor的處理:該類實現了BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor,重寫了 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法與postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法

然后,看springboot啟動流程中的另外一步:
SpringApplication對象的run方法,加載spring容器refreshContext(context);定位到AbstractApplicationContext類的refresh()方法,方法內部流程中有一步invokeBeanFactoryPostProcessors(beanFactory);
內部通過 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 其中最重要的一步是:String[] postProcessorNames =  
beanFactory.getBeanNamesForType( BeanDefinitionRegistryPostProcessor.class, true, false); 遍歷 然后currentRegistryProcessors.add( beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));這里會對所有BeanDefinitionRegistryPostProcessor的實現類進行 getBean實例化的操作( ConfigurationClassPostProcessor在這里得到實例化
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 遍歷調用每一個BeanDefinitionRegistryPostProcessor的實現類的postProcessBeanDefinitionRegistry方法,所以 ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法會得到調用
下面看ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:
processConfigBeanDefinitions(registry); 內部會構建一個ConfigurationClassParser對象 Parse each @Configuration class 通過parser.parse(candidates);定位到processConfigurationClass(ConfigurationClass configClass);再到doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) ;這里會處理 @PropertySource, @ComponentScan@Import@ImportResource@Bean 及  Process default methods on interfaces;// Process any @Import annotations  --->  processImports(configClass, sourceClass, getImports(sourceClass), true); 該方法里會有對@Import注解3種方式ImportSelector實現類和ImportBeanDefinitionRegistrar實現類以及未實現這些接口的類的處理;
if(實現ImportBeanDefinitionRegistrar){
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());后面的ConfigurationClassPostProcessoorl類的processConfigBeanDefinitions方法中的this.reader.loadBeanDefinitions(configClasses)會調用這些ImportBeanDefinitionRegistrar實現類的registerBeanDefinitions方法
}else if(實現ImportSelector){
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 第1步
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false); // 第2步
這里涉及到遞歸調用,第一次進來時,走到第1步,拿到importClassNames執行第2步時,會跳轉到處理沒有實現ImportBeanDefinitionRegistrar和ImportSelector這些接口的普通bean了
}else{
  //對沒有實現ImportBeanDefinitionRegistrar和ImportSelector這些接口的普通bean了
  processConfigurationClass(candidate.asConfigClass(configClass));
}
processImports方法執行完畢,Import注解導入的bean都被保存在ConfigurationClassParser實例中,回到processConfigBeanDefinitions(registry),parse之后,this.reader.loadBeanDefinitions(configClasses);// parse那里准備好了bean信息,這里是真正的處理;ConfigurationClassBeanDefinitionReader類中是對每個配置類逐個執行loadBeanDefinitionsForConfigurationClass方法,
// 普通的類,通過下面的方法將bean定義注冊在spring環境
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 實現了 ImportBeanDefinitionRegistrar接口的實例,會執行
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
 
到此,@Import注解分析完成了,后續接着優化~
 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM