概述
Spring中有各种注解,比如@Configuration、@Import、@ComponentScan、@Autowired等等,那Spring是怎么识别和解析这些注解的呢。是通过BeanFactoryPostProcessor和BeanPostProcessor这两种扩展的机制来实现解析和识别的。那么我们来认识下这些处理注解的各种BeanFactoryPostProcessor和BeanPostProcessor,包括:
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
它们都是通过方法AnnotationConfigUtils#registerAnnotationConfigProcessors()向容器中注册的,因此我们可以得出这么一个结论,无论是通过SpringBoot、xml文件的<context:/>扩展标签还是纯JavaConfig形式的标准应用,最终底层都会调用这个方法向容器注册这些BeanFactoryPostProcessor和BeanPostProcessor的,这样才能识别这些注解起到配置的作用。
ConfigurationClassPostProcessor
ConfigurationClassPostProcessor会处理所有BeanDefinition中的符合注解条件的BeanDefinition,(@Configuration注解的、@Component、@ComponentScan、@Import、@ImportResource或者@Bean注解的),使用ConfigurationClassParser解析出javaconfig配置类,使用ConfigurationClassBeanDefinitionReader对解析出的结果进行加载。
PS:ConfigurationClassPostProcessor是对所有注册到Spring容器中的BeanDefinition进行处理,是已经注册的是关键,不管是手动注册还是扫描机制。
1、过滤出带有相关注解的BeanDefinition
对所有BeanDefinition进行筛查,符合条件的是带有注解@Configuration、@Component、@ComponentScan、@Import、@ImportResource或者@Bean注解的。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}
2、对第一步的候选类进一步处理
带有@Configuration注解的Bean Class上面的注解@ComponentScan、@Import、@ImportResource、@Bean这些注解是怎么解析的呢,就是使用ConfigurationClassParser类来依依解析的。根据注解的不同进行不同的处理,目的是找出所有的配置类。我们抽出重要的部分看下,对应方法为processConfigurationClass():
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;}
<1>、处理@ComponentScan
直接使用ClassPathBeanDefinitionScanner进行扫描,对扫描出的候选配置类在进行步骤2的处理
// Process any @ComponentScan annotationsAnnotationAttributes 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扫描 SetscannedBeanDefinitions = 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()); } }}
<2>、处理@Import
找出所有Import注解(包括注解的层级关系,父子注解)的属性Class,根据Class的类型不同进行不同处理,对应方法processImports()
2.1、ImportSelector
如果类型是ImportSelector,会创建ImportSelector的实例调用selectImports方法,将返回值作为@Import注解的class属性,递归调用processImports()
2.2、ImportBeanDefinitionRegistrar
如果类型是ImportBeanDefinitionRegistrar,创建ImportBeanDefinitionRegistrar的对象先加入importBeanDefinitionRegistrars属性中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
2.3、其他类型
如果不是,也不是,就把当当成@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()); CollectionimportSourceClasses = 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)); }}
<3>、处理@ImportResource
分别获取@ImportResource设置的location属性,放入到importedResources中
AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); }}
<4>、处理@Bean
带有@Bean注解的方法,都加入到beanMethods中
// Process individual @Bean methodsSetbeanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}
3、解析出的结果进行加载
使用ConfigurationClassParser解析每个带有@Configuration注解的Bean的类之后,这个类上面的@ComponentScan、@Import、@ImportResource、@Bean这些注解都已经被解析过了。需要使用
ConfigurationClassBeanDefinitionReader来将解析的结果加载到Spring容器中。
public void loadBeanDefinitions(SetconfigurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); }}private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { // // if (configClass.isImported()) { // 1、这个表示这个Class是被其他@Configuration的类导入的(使用@Import),就这个Class定义为BeanDefinition注册到Spring容器中 registerBeanDefinitionForImportedConfigurationClass(configClass); } 2、每个@Bean注解的方法,解析@Bean的属性,并且将这个方法定义为BeanDefinition注册到Spring容器中 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } 3、加载@ImportResource定义的配置文件,xml格式或者groovy格式 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); 4、调用每个ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,由ImportBeanDefinitionRegistrar来实现注册的逻辑 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}
说明:
- 如果这个ConfigClass是被其他@Configuration的类导入的(使用@Import),就把这个ConfigClass注册到Spring容器中;
- 每个@Bean注解的方法,根据@Bean的属性,并且将这个方法定义为BeanDefinition注册到Spring容器中;
- 加载@ImportResource定义的配置文件,xml格式或者groovy格式;
- 调用每个ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,由ImportBeanDefinitionRegistrar来实现注册的逻辑;
总结
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的逻辑。