博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring注解解析之ConfigurationClassPostProcessor
阅读量:5814 次
发布时间:2019-06-18

本文共 8515 字,大约阅读时间需要 28 分钟。

  hot3.png

概述

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扫描	Set
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()); } }}

 

<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()); Collection
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)); }}

 

<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 methodsSet
beanMethods = 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(Set
configurationModel) { 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());}

说明:

  1. 如果这个ConfigClass是被其他@Configuration的类导入的(使用@Import),就把这个ConfigClass注册到Spring容器中;
  2. 每个@Bean注解的方法,根据@Bean的属性,并且将这个方法定义为BeanDefinition注册到Spring容器中;
  3. 加载@ImportResource定义的配置文件,xml格式或者groovy格式;
  4. 调用每个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的逻辑。

 

转载于:https://my.oschina.net/cregu/blog/3041549

你可能感兴趣的文章
nginx : TCP代理和负载均衡的stream模块
查看>>
MYSQL数据库间同步数据
查看>>
DevOps 前世今生 | mPaaS 线上直播 CodeHub #1 回顾
查看>>
iOS 解决UITabelView刷新闪动
查看>>
让前端小姐姐愉快地开发表单
查看>>
Dubbo笔记(四)
查看>>
Web前端JQuery入门实战案例
查看>>
java B2B2C Springboot电子商城系统- SSO单点登录之OAuth2.0 登出流程(3)
查看>>
USB 通信原理
查看>>
7zZip zip RAR iOS
查看>>
date命令的详细用法!
查看>>
UiAutomator源码分析之UiAutomatorBridge框架
查看>>
python 开发之selenium
查看>>
Xcode3.2.5中找不到Mac OS X - Command Line Utility -...
查看>>
css的div垂直居中的方法,百分比div垂直居中
查看>>
如何理解EM算法
查看>>
nginx 域名跳转一例~~~(rewrite、proxy)
查看>>
linux用户家目录无损迁移到独立硬盘
查看>>
文件查找
查看>>
shell编程前言(一)
查看>>