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註解的處理過程大致可以描述為:
尋找BeanFactory中所有被@Configuration註解修飾的類,包括被@Configuration派生註解修飾的類。
尋找被@Configuration註解修飾的類上的所有註解元資訊(這裡的搜尋不僅是搜尋當前註解,還會迭代往修飾註解的註解的註解上層…..一直搜尋@Import,直到註解最原始的註解),獲取@Import註解的匯入類資訊,如果沒有則不處理。
根據匯入類的資訊,判定為
- 普通配置類,裡面包含@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)