引言

Spring容器中提供很多方便的注解供我们在工作中使用,比如@Configuration注解,里面可以在方法上定义@Bean注解,将调用方法返回的对象交由Bean容器进行管理,那么Spring框架是如何处理@Configuration注解的呢

源码

/**
* 此类是一个后置处理器的类,主要功能是参与BeanFactory的建造,主要功能如下
* 1、解析加了@Configuration的配置类
* 2、解析@ComponentScan扫描的包
* 3、解析@ComponentScans扫描的包
* 4、解析@Import注解
*
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
* {@link Configuration @Configuration} classes.
*
* <p>Registered by default when using {@code <context:annotation-config/>} or
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
* with any other BeanFactoryPostProcessor.
*
* <p>This post processor is priority-ordered as it is important that any
* {@link Bean} methods declared in {@code @Configuration} classes have
* their corresponding bean definitions registered before any other
* {@link BeanFactoryPostProcessor} executes.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

通过阅读源码我们可以得知 ConfigurationClassPostProcessor 属于BFPP中的BDRPP,关于BFPP和BDRPP,请参考另一篇Spring扩展——BeanFactoryPostProcessor(BFPP)

其类结构图如下

通过对BFPP或BDRPP的理解,得出ConfigurationClassPostProcessor会在Spring容器启动流程中invokeBeanFactoryPostProcessors(beanFactory)方法执行过程中被调用,先会执行其postProcessBeanDefinitionRegistry(registry)方法,引入其它Bean定义信息,再执行BFPP的postProcessBeanFactory(beanFactory)方法。

ConfigurationClassPostProcessor作为Spring框架的内部BFPP/BDRPP,我们先看一下,它在什么时候什么情况下,会被自动加入到Spring容器中?

Spring创建ConfigurationClassPostProcessor

  1. 对于ClassPathXmlApplicationContext,它会在loadBeanDefinitions——》解析XML文档标签context:component-scan——》调用相应的Handler进行parse——通过AnnotationConfigUtils.registerAnnotationConfigProcessors()静态方法将ConfigurationClassPostProcessor加入到容器中。

  2. 对于ClassPathXmlApplicationContext,在解析context:annotation-config</context:annotation-config>的时候,也会通过AnnotationConfigUtils.registerAnnotationConfigProcessors将其加入到容器中来

  3. 对于AnnotationConfigApplicationContext,在新建的时候,会初始化内部的AnnotatedBeanDefinitionReader成员变量,在创建AnnotatedBeanDefinitionReader的时候,就会调用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)将ConfigurationClassPostProcessor加入到容器中来。

综上所述,在XML文件中使用component-scan 和 context:annotation-config 标签的时候会将ConfigurationClassPostProcessor加入到Spring容器中来,在基于纯属注解的方式,默认的情况下,在初始化上下文环境的时候,也会将其加入进来。

在Spring启动过程中,在率先执行BFPP的相关方法,而执行的时候,会先执行BDRPP的相关方法,因为可以发现更多的其它的BDRPP,保证容器中内部的 和 自定义的所有BFPP或BDRPP都能够得到执行。

执行postProcessBeanDefinitionRegistry()



在这个方法中处理流程比较复杂,经过了多次递归调用

对于每一个被Configuration注解标注的类而言,它可能被标注其它如ComponentScan或ComponentScans或Import注解,而这些注解的功能,是引入更多的Bean定义信息,而它引入的Bean也有可能被标注如Configuration、ComponentScan、ComponentScans、Import等注解,所以,这个地方为保证所有定义的Bean定义信息能够被识别到,就需要进行递归的调用。

在进入到parse后

1、处理@PropertySources注解,配置信息的解析

2、处理@ComponentScan注解:使用ComponentScanAnnotationParser扫描basePackage下的需要解析的类,并注册到BeanFactory中(这个时候bean并没有进行实例化,而是进行了注册。具体的实例化在finishBeanFactoryInitialization方法中执行)。对于扫描出来的类,递归解析

3、处理@Import注解:先递归找出所有的注解,然后再过滤出只有@Import注解的类,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解数据的话,首先发现@SpringBootApplication不是一个@Import注解,然后递归调用修饰了@SpringBootApplication的注解,发现有个@EnableAutoConfiguration注解,再次递归发现被@Import(EnableAutoConfigurationImportSelector.class)修饰,还有@AutoConfigurationPackage注解修饰,再次递归@AutoConfigurationPackage注解,发现被@Import(AutoConfigurationPackages.Registrar.class)注解修饰,所以@SpringBootApplication注解对应的@Import注解有2个,分别是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。找出所有的@Import注解之后,开始处理逻辑:

   (1)、遍历这些@Import注解内部的属性类集合

   (2)、如果这个类是个ImportSelector接口的实现类,实例化这个ImportSelector,如果这个类也是DeferredImportSelector接口的实现类,那么加入ConfigurationClassParser的deferredImportSelectors属性中让第6步处理。否则调用ImportSelector的selectImports方法得到需要Import的类,然后对这些类递归做@Import注解的处理

   (3)、如果这个类是ImportBeanDefinitionRegistrar接口的实现类,设置到配置类ConfigurationClass的importBeanDefinitionRegistrars属性中

  

   (4)、其它情况下把这个类入队到ConfigurationClassParser的importStack(队列)属性中,然后把这个类当成是@Configuration注解修饰的类递归重头开始解析这个类

4、处理@ImportResource注解:获取@ImportResource注解的locations属性,得到资源文件的地址信息。然后遍历这些资源文件并把它们添加到配置类的importedResources属性中

5、处理@Bean注解:获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中

6、处理DeferredImportSelector:处理第3步@Import注解产生的DeferredImportSelector,进行selectImports方法的调用找出需要import的类,然后再调用第3步相同的处理逻辑处理

在以上步骤中,所解析到的注解

@Configuration

首先,他会获取@Configuration注解,他实际上是继承了@Component。然后再解析@Configuration类中的其他注解。所以我们经常在类上写的@Configuration之所以会注入到容器中,就是这里被解析的。

解析的过程是在ConfigurationClassParser的parser方法中,解析的结果存入BeanDefinition。parser最后调用比较重要的方法是doProcessConfigurationClass。

@Conditional

doProcessConfigurationClass方法是在processConfigurationClass方法中调用的,processConfigurationClass方法中有一个比较重要的注解判断,@Conditional,用于判断是否存入BeanDefinition。我们常用的@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnClass、@ConditionalOnMissingClass、@Conditional等就是ConditionEvaluator#shouldSkip方法来判断的,如果符合条件,才可以解析下面的几个注解。

@PropertySource

当需要引入资源配置文件的时候,经常用以下的写法,它能被注入到各个属性,就是在doProcessConfigurationClass这个方法中实现的。

@Configuration
@PropertySource("classpath:xxx.properties")
public class XXXConfig { }

@ComponentScans和@ComponentScan

这两个注解的basePackages下面的类,就是这里在这里扫描,由于可能扫描的类中,也有这两个注解,所以这个方法里会通过递归调用parse方法。

@Import

Import注解可以引入普通类,也可以引入ImportSelector接口的类,也可以引入ImportBeanDefinitionRegistrar接口的类

解析的时候,首先是解析ImportSelector接口,然后是ImportBeanDefinitionRegistrar接口,最后是普通类。

@Bean

@Bean会在解析完Configuration注解后,将解析其中被@Bean注解标注了的方法,通过方法也会返回一个Bean定义信息。

执行postProcessBeanFactory()

方法源码如下

	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
} //对@Configuration标注了的类,进行动态代理
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

通过postProcessBeanFactory这个方法,会对容器中所有的@Configuration注解标注了的类,进行动态代理。

大家可能会问,这个地方为什么需要动态代理?

首先我们@Configuration注解标注了的类,本身也是作为一个bean对象注册到容器中的,通过这个对象,我们可以调用被@Bean注解了的方法,如果不做动态代理的话,当调用@Bean注解了的方法时,会直接运行方法体内容,并返回一个对象。Spring为我们考虑得非常周到,当通过动态代理的方式去调用@Bean注解标注了的方法时,就会从容器中获取相应的Bean,确保Spring容器中的单例bean对象的单例性。所以在ConfigurationClassPostProcessor类中,这个方法最核心的作用就是为所有的Conguration注解标注了的类,生成其对应的代理对象。

Spring源码——ConfigurationClassPostProcessor类的更多相关文章

  1. spring源码 RootBeanDefinition类的根接口AttributeAccessor

    /** * Interface defining a generic contract for attaching and accessing metadata * to/from arbitrary ...

  2. Spring源码解析之ConfigurationClassPostProcessor(二)

    上一个章节,笔者向大家介绍了spring是如何来过滤配置类的,下面我们来看看在过滤出配置类后,spring是如何来解析配置类的.首先过滤出来的配置类会存放在configCandidates列表, 在代 ...

  3. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  4. Spring源码分析——BeanFactory体系之抽象类、类分析(一)

    上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...

  5. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  6. Spring源码情操陶冶-AOP之Advice通知类解析与使用

    阅读本文请先稍微浏览下上篇文章Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器,本文则对aop模式的通知类作简单的分析 入口 根据前文讲解,我们知道通知类的 ...

  7. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  8. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

  9. Spring源码分析(三)容器核心类

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇文章中,我们熟悉了容器的基本用法.在这一篇,我们开始分析Spri ...

  10. Spring源码解读:核心类DefaultListableBeanFactory的继承体系

    1 简介 我们常用的ClassPathXmlApplicationContext是AbstractRefreshableApplicationContext的子类,而DefaultListableBe ...

随机推荐

  1. Vite + React 组件开发实践

    简介: 毫不夸张的说,Vite 给前端带来的绝对是一次革命性的变化.或者也可以说是 Vite 背后整合的 esbuild . Browser es modules.HMR.Pre-Bundling 等 ...

  2. 基于 Scheduled SQL 对 VPC FlowLog 实现细粒度时间窗口分析

    简介: 针对VPC FlowLog的五元组和捕获窗口信息,在分析时使用不同时间窗口精度,可能得到不一样的流量特征,本文介绍一种方法将原始采集日志的时间窗口做拆分,之后重新聚合为新的日志做分析,达到更细 ...

  3. 带你体验云原生场景下 Serverless 应用编程模型

    ​简介: 阿里云 Knative 基于 ASK 之上,在完全兼容社区 Knaitve 的同时对 FC.ECI 工作负载进行统一应用编排,支持事件驱动.自动弹性,为您提供统一的 Serverless 应 ...

  4. 鸿蒙HarmonyOS实战-ArkUI动画(页面转场动画)

    前言 页面转场动画是指在应用程序中,当用户导航到另一个页面时,使用动画效果来过渡页面之间的切换.这样做的目的是为了提升用户体验,使页面之间的切换更加平滑和有趣. 常见的页面转场动画包括淡入淡出.滑动. ...

  5. Region-区域

    定义Region的方式有两种: 一种是在XAML定义 RegionManager.RegionName(XAML) 一.View代码 1 <Viewbox Grid.Column="1 ...

  6. DelegateCommand-最简单的合令调用。

    View代码 <StackPanel> <Button Content="方法一" Command="{Binding AddCommand}" ...

  7. 2019-8-31-NuGet-如何设置图标

    title author date CreateTime categories NuGet 如何设置图标 lindexi 2019-08-31 16:55:58 +0800 2019-4-27 17: ...

  8. 2021年5.21NCU第四届校赛

    比赛地址:http://222.204.50.106/contest/39 A 树上祖先 链接:http://222.204.50.106/contest/39/problem/A B 莎士比亚 链接 ...

  9. 【爬虫数据集】李子柒YouTube频道TOP10热门视频的TOP2000热门评论,共计2W条

    目录 一.背景 二.爬取目标 三.结果展示 四.演示视频 五.附完整数据 一.背景 这段时间,有超多小伙伴找我要YouTube数据,做数据分析.情感分析之类的研究工作,但很多人并不是计算机软件相关专业 ...

  10. centos 7 开启 httpd 服务和 80 端口

    centos 7 开启 httpd 服务和 80 端口 yum install -y httpd systemctl start httpd firewall-cmd --add-service=ht ...