Spring中有各類註解,好比@Configuration、@Import、@ComponentScan、@Autowired等等,那Spring是怎麼識別和解析這些註解的呢。是經過BeanFactoryPostProcessor和BeanPostProcessor這兩種擴展的機制來實現解析和識別的。那麼咱們來認識下這些處理註解的各類BeanFactoryPostProcessor和BeanPostProcessor,包括:java
它們都是經過方法AnnotationConfigUtils#registerAnnotationConfigProcessors()向容器中註冊的,所以咱們能夠得出這麼一個結論,不管是經過SpringBoot、xml文件的<context:/>擴展標籤仍是純JavaConfig形式的標準應用,最終底層都會調用這個方法向容器註冊這些BeanFactoryPostProcessor和BeanPostProcessor的,這樣才能識別這些註解起到配置的做用。ui
ConfigurationClassPostProcessor會處理全部BeanDefinition中的符合註解條件的BeanDefinition,(@Configuration註解的、@Component、@ComponentScan、@Import、@ImportResource或者@Bean註解的),使用ConfigurationClassParser解析出javaconfig配置類,使用ConfigurationClassBeanDefinitionReader對解析出的結果進行加載。this
PS:ConfigurationClassPostProcessor是對全部註冊到Spring容器中的BeanDefinition進行處理,是已經註冊的是關鍵,無論是手動註冊仍是掃描機制。lua
對全部BeanDefinition進行篩查,符合條件的是帶有註解@Configuration、@Component、@ComponentScan、@Import、@ImportResource或者@Bean註解的。spa
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); }
帶有@Configuration註解的Bean Class上面的註解@ComponentScan、@Import、@ImportResource、@Bean這些註解是怎麼解析的呢,就是使用ConfigurationClassParser類來依依解析的。根據註解的不一樣進行不一樣的處理,目的是找出全部的配置類。咱們抽出重要的部分看下,對應方法爲processConfigurationClass():code
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first ... // Process any @PropertySource annotations ... // Process any @ComponentScan annotations <1>、處理@ComponentScan註解 // Process any @Import annotations <2>、處理@Import註解 // Process any @ImportResource annotations <3>、處理@ImportResource註解 // Process individual @Bean methods <4>、處理單獨的@Bean註解方法 // Process superclass, if any ... // No superclass -> processing is complete return null; }
直接使用ClassPathBeanDefinitionScanner進行掃描,對掃描出的候選配置類在進行步驟2的處理component
// Process any @ComponentScan annotations AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class); if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // The config class is annotated with @ComponentScan -> perform the scan immediately // 使用ClassPathBeanDefinitionScanner掃描 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if necessary for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) { // 若是是配置類,遞歸處理 parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } }
找出全部Import註解(包括註解的層級關係,父子註解)的屬性Class,根據Class的類型不一樣進行不一樣處理,對應方法processImports()orm
2.一、ImportSelectorxml
若是類型是ImportSelector,會建立ImportSelector的實例調用selectImports方法,將返回值做爲@Import註解的class屬性,遞歸調用processImports()對象
2.二、ImportBeanDefinitionRegistrar
若是類型是ImportBeanDefinitionRegistrar,建立ImportBeanDefinitionRegistrar的對象先加入importBeanDefinitionRegistrars屬性中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
2.三、其餘類型
若是不是,也不是,就把噹噹成@Configuration類型的,調用processConfigurationClass()從新處理
for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } 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); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } }
分別獲取@ImportResource設置的location屬性,放入到importedResources中
AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } }
帶有@Bean註解的方法,都加入到beanMethods中
// Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); }
使用ConfigurationClassParser解析每一個帶有@Configuration註解的Bean的類以後,這個類上面的@ComponentScan、@Import、@ImportResource、@Bean這些註解都已經被解析過了。須要使用
ConfigurationClassBeanDefinitionReader來將解析的結果加載到Spring容器中。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } } private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { // // if (configClass.isImported()) { // 一、這個表示這個Class是被其餘@Configuration的類導入的(使用@Import),就這個Class定義爲BeanDefinition註冊到Spring容器中 registerBeanDefinitionForImportedConfigurationClass(configClass); } 二、每一個@Bean註解的方法,解析@Bean的屬性,而且將這個方法定義爲BeanDefinition註冊到Spring容器中 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } 三、加載@ImportResource定義的配置文件,xml格式或者groovy格式 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); 四、調用每一個ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,由ImportBeanDefinitionRegistrar來實現註冊的邏輯 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
說明:
Spring中的@Configuration、@ComponentScan、@Import、@ImportResource、@Bean這些註解是ConfigurationClassPostProcessor來負責處理的。ConfigurationClassPostProcessor是BeanFactoryPostProcessor,在Spring註冊完全部的BeanDefinition以後實例化Bean以前進行擴展。ConfigurationClassPostProcessor負責處理這些,其中根據註解的不一樣可能會向Spring註冊新的BeanDefinition。
@Configuration註解描述的類自己會被註冊到容器,@Bean描述的方法會被註冊到容器,@ImportResource描述的配置文件會被加載會解析出BeanDefinition註冊到Spring中
@Import導入的普通類會當作ConfigClass處理,@Import導入的ImportSelector類會執行ImportSelector#selectImports()方法,方法返回的類也做爲導入進行處理@Import的邏輯,@Import導入的ImportBeanDefinitionRegistrar類,會調用它的registerBeanDefinitions()方法,由它來自定義註冊BeanDefinition的邏輯。