Spring框架源码(五) @configuration源码深度解析

news2025/7/14 17:50:14

        @Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解,开发者可以通过@Configuration注解来定义配置类,也可以使用xml形式注入。

        例如配置数据库配置,定义一个配置类,注入数据源DataSource, 事务管理器TransactionManager, 多数据源管理等都可以使用@Configuration 类来标记该类是一个配置类,Spring 框架会扫描并解析该类里的Bean 并注入,下面就如何解析配置类源码解析。

        官方源码解释:

        1. Indicates that  a class declares one or more @Bean methods and may be processed by the spring container  to generate bean definitions and service requests for those  beans at runtime.

       表示一个类声明了一个或多个被@Bean注解标记的方法, 他们应该会在运行时被Spring 容器处理并会为这些Bean产生bean definitions 和服务请求。

 @Configuration
   public class AppConfig {
  
       @Bean
       public MyBean myBean() {
           // instantiate, configure and return bean ...
       }
   }

        简单讲: @Configuration 标记的类可以用@Bean 定义很多Bean method 。 

一、@Configuration 用法

1. 注解形式@Configuration

   @Configuration
   public class AppConfig {
  
       @Bean
       public MyBean myBean() {
           // instantiate, configure and return bean ...
       }
   }

        使用AnnotationConfigurationApplicationContext初始化配置类。 

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(AppConfig.class);
   ctx.refresh();
   MyBean myBean = ctx.getBean(MyBean.class);
   // use myBean ...

2. xml形式

       使用<context:annotation-config >属性声明, xml形式需要使用ClassPathXmlApplicationContext来加载。


   <beans>
      <context:annotation-config/>
      <bean class="com.acme.AppConfig"/>
   </beans>

        官方: In the example above, <context: annotation-config> is required in order to enable ConfigurationClassPostProcessor

        我们从官方解释中看到,根据配置会进入到ConfigurationClassPostProcessor,那ConfigurationClassPostProcessor 会是扫描这些配置Bean的入口。

二、ConfigurationClassPostProcessor源码解析

         ConfigurationClassPostProcessor 类实现了BeanFacotryPostProcessor和BeanDefinitionRegistryPostProcessor 两个接口, 是解析@Configuration类的入口。

        1. BeanFactoryPostProcessor: PostProcessBeanFactory 方法入参为ConfigurableListableBeanFactory, ConfigurableListableBeanFactory的默认实现为DefaultListableBeanFactory, 提供

        2.BeanDefinitionRegistryPostProcessor:  postProcessBeanDefinitionRegistry()方法入参为BeanDefinitionRegistry。该方法的作用主要是扫描并注册所有@Configuraiton注解标记的类下的bean。

         先进入到processConfigBeanDefinitions方法,看看做了哪些事?

        这里我们可以发现遍历所有的BeanDefinition, 过滤掉已经处理完的ConfigurationClass. 用isFullConfigurationClass和isLiteConfigurationClass来判断。

         如果已经设置了候选人身份,那么该判断就为true, 也就不会继续进行放入到configCondidates里。

        如果之前没有设置候选人身份,那么在执行CheckConfigurationClassCandidate时,会检查是否为FullConfigurationCandidate和LiteConfigurationCandidate, 接着看FullConfigurationCandidate和LiteConfigurationCandidate。

FullConfigurationCandidate和LiteConfigurationCandidate

        从字面意思上理解,一个为满,一个为简的候选人,那他们真正的含义是什么呢?

        FullConfigurationCandidate  直接被@Configuration注解标记的类被称为FullConfigurationCandidate。

	public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
		return metadata.isAnnotated(Configuration.class.getName());
	}

         LiteConfigurationCandidate 主要是指被@Component、@ComponentScan、@Import、@ImportSource注解标记的类。

	private static final Set<String> candidateIndicators = new HashSet<>(8);

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}


public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		if (metadata.isInterface()) {
			return false;
		}

		// Any of the typical annotations found?
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		try {
			return metadata.hasAnnotatedMethods(Bean.class.getName());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
			}
			return false;
		}
	}

         如果是FullConfigurationCandidate或者LiteConfigurationCandidate, 那么给该Definition打个标记表示该BeanDefinition已经候选过, 那下次扫描的时候就会跳过该BeanDefinition。


		if (isFullConfigurationCandidate(metadata)) {
         // 表示已经候选过
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		else if (isLiteConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}

        走到这里,configCandidates有一个值了,该值我配置的MybatisConfig类:

         接下来就是解析候选ConfigurationClass

解析所有的候选ConfigurationClass并加载所有的Bean

1.  由ConfigurationClassParser 解析所有的candidates。

2.  由ConfigurationClassBeanDefinitionReader 来解析该configurationClass里的所有Bean。

3.  再次检查是否有新的ConfigurationClass加入到candidates集合里,如果有那么继续解析并加载所有的Bean。

源码如下:  

do {
			//解析所有的候选Bean
			parser.parse(candidates);
			//对ConfigurationClass校验, 如果是Final类会报错。
			parser.validate();

			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());
			}
			// 加载所有的BeanDefinitions
			this.reader.loadBeanDefinitions(configClasses);
			// 已经解析了加入到alreadyParsed集合了
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			// 再次检查以防有新的ConfigurationClass 加入
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							// 补偿未添加上的Candidates
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

三、@Configuration配合Profile使用

        官方源码给出了一个用法例子,对于不同的开发环境可以使用@Profile注解来指定配置文件。

 @Profile("development")
   @Configuration
   public class EmbeddedDatabaseConfig {
  
       @Bean
       public DataSource dataSource() {
           // instantiate, configure and return embedded DataSource
       }
   }
  
   @Profile("production")
   @Configuration
   public class ProductionDatabaseConfig {
  
       @Bean
       public DataSource dataSource() {
           // instantiate, configure and return production DataSource
       }
   }

        spring boot项目使用spring.profiles.active=development来指定激活的profile, 指定哪个就用哪个。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

类和对象(下)(二)

类和对象&#xff08;下&#xff09;&#xff08;二&#xff09;1.友元1.1友元函数1.2友元类2.内部类3.拷贝对象时的一些编译器优化&#xff08;vs2022&#xff09;&#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680…

【c#】反射学习笔记01

c#反射学习01反射学习一、反射原理二、那么我们是如何通过metadata来实现反射呢&#xff1f;三、反射的好处四、反射创建对象&#xff08;利用配置文件简单工厂反射&#xff09;五、反射的黑科技&#xff08;多构造函数调用、破坏单例、创建泛型&#xff09;六、反射调用实例方…

环境配置完整指导——Installing C++ Distributions of PyTorch

目录一、前言二、动手开始做1. 安装cuda 11.42. 安装visual studio 2019 community3. 安装libtorch4. 安装mingw-w645. 配置环境变量6. 打开vscode开始写程序7. 运行程序8. 其他报错信息文章简介&#xff1a;这篇文章用于介绍在windows10 vscode中&#xff0c;跑通如下代码的全…

JavaScript 教程导读

JavaScript 是 Web 的编程语言。所有现代的 HTML 页面都使用 JavaScript&#xff0c;可以用于改进设计、验证表单、检测浏览器、创建cookies等。JavaScript 非常容易学。本教程将教你学习从初级到高级JavaScript知识。JavaScript 在线实例本教程包含了大量的 JavaScript 实例&a…

使用 ONLYOFFICE 转换 API 构建在线文档转换器

文档转换是非常常用、非常有价值的功能&#xff0c;可以帮助我们处理多种文档类型。ONLYOFFICE 编辑器可以轻松地将文档转换为多种格式。在这篇博文中&#xff0c;我们会向您展示&#xff0c;如何构建在 ONLYOFFICE 转换 API 上运行的在线转换器。 关于 ONLYOFFICE 转换 API 使…

傻白探索Chiplet,Design Space Exploration for Chiplet-Assembly-Based Processors(十三)

阅读了Design Space Exploration for Chiplet-Assembly-Based Processors这篇论文&#xff0c;是关于chiplet设计空间探索的&#xff0c;个人感觉核心贡献有两个&#xff1a;1.提出使用整数线性规划算法进行Chiplet的选择&#xff1b;2.基于RE和NRE提出了一个cost模型&#xff…

Map和Set(Java详解)

在开始详解之前&#xff0c;先来看看集合的框架&#xff1a; 可以看到Set实现了Collection接口&#xff0c;而Map又是一个单独存在的接口。 而最下面又分别各有两个类&#xff0c;分别是TreeSet&#xff08;Map&#xff09;和 HashSet&#xff08;Map&#xff09;。 TreeSet&…

CSS---动态向下的循环箭头动画效果

介绍 在移动端的页面中&#xff0c;经常有翻页的提示效果&#xff0c;经常使用向下的箭头动画来提示&#xff1b;一般效果如下所示&#xff1a; 使用到的图片 使用到的图片&#xff0c;就是一个向下的箭头&#xff1b;这里可以下载我的图片使用&#xff1b; 或者也可以使用…

92.【SpringCloud NetFilx】

SpringCloud(一)、这个阶段该如何学习?1.微服务介绍2.面试常见问题(二)、微服务概述1.什么是微服务?2. 微服务与微服务架构(1).微服务(2).微服务架构⭐(3). 微服务优缺点(4). 微服务技术栈有那些&#xff1f;(5). 为什么选择SpringCloud作为微服务架构(三)、SpringCloud入门概…

Python 之 Matplotlib 第一个绘图程序和基本方法

文章目录一、第一个 Matplotlib 绘图程序1. Matplotlib 绘图的基本步骤二、Matplotlib 的基本方法1. 图表名称 plt.title()2. x 轴和 y 轴名称3. 设置 x 轴和 y 轴的刻度4. 显示图表 show()5. 图例 legend()6. 图例的图例位置设置7. 显示每条数据的值 x,y 值的位置一、第一个 M…

LeetCode 61. 旋转链表

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给你一个链表的头节点 headheadhead &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 kkk 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1…

wafw00f源码及流量特征分析

wafw00f介绍 这不是本次的重点&#xff0c;相关介绍及使用方法相信大家已经了解&#xff0c;所以此处就直接引用其开发者对该工具的介绍。 To do its magic, WAFW00F does the following: Sends a normal HTTP request and analyses the response; this identifies a number o…

(考研湖科大教书匠计算机网络)第四章网络层-第三节3、4:划分子网的IPv4地址和无分类IP地址

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;划分子网的IPv4地址&#xff08;1&#xff09;划分子网思想&#xff08;2&#xff09;子网掩码A&#xff1a;概述B&#xff1a;例子C&#xff1a;默…

Django 模型继承问题

文章目录Django 模型继承问题继承出现的情况Meta 和多表继承Meta 和多表继承继承与反向关系指定父类连接字段代理模型QuerySet 仍会返回请求的模型基类约束代理模型管理器代理继承和未托管的模型间的区别多重继承不能用字段名 "hiding"在一个包中管理模型Django 模型…

linux安装极狐gitlab

1. 官网寻找安装方式 不管我们使用任何软件&#xff0c;最靠谱的方式就是查看官方文档。gitlab提供了相应的安装文档&#xff0c;并且有对应的中文文档。地址如下&#xff1a; https://gitlab.cn/install/ 我在这里以CentOS作为安装示例&#xff0c;大家可根据自己的需要选择…

LabVIEW中ActiveX控件、ActiveX服务器和类型库注册

LabVIEW中ActiveX控件、ActiveX服务器和类型库注册如何在计算机上手动注册ActiveX控件&#xff08;.ocx &#xff09;、ActiveX服务器&#xff08;.DLL和.EXE&#xff09;以及类型库&#xff08;.TLB &#xff09;&#xff1f;在LabVIEW中打开ActiveX控件或类的引用时&#xff…

XCP实战系列介绍12-基于Vector_Davinci工具的XCP配置介绍(一)

本文框架 1.概述2. EcuC配置2.1 Pdu添加步骤2.2 配置项说明3. Can 模块配置4. CanIf 模块配置4.1 接收帧的Hardware Receive Object配置4.2 接收帧和发送帧的Pdu配置1.概述 在文章《看了就会的XCP协议介绍》中详细介绍了XCP的协议,在《XCP实战系列介绍01-测量与标定底层逻辑》…

ELK分布式日志收集快速入门-(二)kafka进阶-快速安装可视化管理界面-(单节点部署)

目录安装前准备安装中安装成功安装前准备 安装kafka-参考博客 (10条消息) ELK分布式日志收集快速入门-&#xff08;一&#xff09;-kafka单体篇_康世行的博客-CSDN博客 安装zk 参考博客 (10条消息) 快速搭建-分布式远程调用框架搭建-dubbozookperspringboot demo 演示_康世行的…

Python编程 动态爱心

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.所用库 1.random简介 2.math 简介 3.tkinter库的简介 二.实际图 三.…

OKR之剑·实战篇06:OKR致胜法宝-氛围业绩双轮驱动(下)

作者&#xff1a;vivo 互联网平台产品研发团队 本文是《OKR 之剑》系列之实战第 6 篇—— 本文介绍团队营造氛围的方法与实践、在业绩方面的探索与输出&#xff0c;在两方面分别总结了一些经验分享给大家。 一、我们营造氛围的方法与实践 先说说氛围。组织氛围的提出者库尔…