Spring配置类解析与Bean扫描过程源码分析

news2024/12/23 13:43:02

文章目录

  • 一、注册ConfigurationClassPostProcessor
  • 二、postProcessBeanDefinitionRegistry方法
    • 1、processConfigBeanDefinitions方法
    • 2、流程梳理
    • 3、postProcessBeanFactory方法
  • 后记

一、注册ConfigurationClassPostProcessor

Spring启动之前,构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)。

此时,会向BeanFactory中添加ConfigurationClassPostProcessor对应的BeanDefinition。

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,并实现了postProcessBeanDefinitionRegistrypostProcessBeanFactory方法。

当Spring容器调用invokeBeanFactoryPostProcessors方法时,就会执行ConfigurationClassPostProcessorBeanDefinitionRegistryPostProcessor方法与postProcessBeanFactory方法。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}

	@Override
	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);
		}

		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}


}

二、postProcessBeanDefinitionRegistry方法

1、processConfigBeanDefinitions方法

该方法调用了ConfigurationClassUtils.checkConfigurationClassCandidate方法,判断一个类是否是配置类:

// org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate
public static boolean checkConfigurationClassCandidate(
		BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

	String className = beanDef.getBeanClassName();
	if (className == null || beanDef.getFactoryMethodName() != null) {
		return false;
	}

	AnnotationMetadata metadata;
	if (beanDef instanceof AnnotatedBeanDefinition &&
			className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
		// Can reuse the pre-parsed metadata from the given BeanDefinition...
		metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
	}
	else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
		// Check already loaded Class if present...
		// since we possibly can't even load the class file for this Class.
		Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
		if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
				BeanPostProcessor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
				EventListenerFactory.class.isAssignableFrom(beanClass)) {
			return false;
		}
		metadata = AnnotationMetadata.introspect(beanClass);
	}
	else {
		try {
			MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
			metadata = metadataReader.getAnnotationMetadata();
		}
		catch (IOException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not find class file for introspecting configuration annotations: " +
						className, ex);
			}
			return false;
		}
	}

	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (config != null || isConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}

	// It's a full or lite configuration candidate... Let's determine the order value, if any.
	Integer order = getOrder(metadata);
	if (order != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, order);
	}

	return true;
}

Spring认为,包含@Component、@ComponentScan、@Import、@ImportResource、@Bean方法的类,属于轻配置类。
而加了@Configuration的类属于full配置类。
在这里插入图片描述

2、流程梳理

1、在启动Spring时,需要传入一个AppConfig.class给ApplicationContext,ApplicationContext会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类BeanDefinition。

2、ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来

3、构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对象ConfigurationClass

4、如果配置类上存在@Component注解,那么解析配置类中的内部类(这里有递归,如果内部类也是配置类的话)

5、如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象,并添加到environment中去

6、如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该配置类,(也有递归),并且会生成对应的ConfigurationClass

7、如果配置类上存在@Import注解,那么则判断Import的类的类型:
如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做配置类进行解析**(也是递归)**
如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar实例对象,并添加到配置类对象中(ConfigurationClass)的importBeanDefinitionRegistrars属性中。

8、如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的importedResources属性中。

9、如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置类对象中的beanMethods属性中。

10、如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法

11、如果配置类有父类,则把父类当做配置类进行解析

12、AppConfig这个配置类会对应一个ConfigurationClass,同时在解析的过程中也会生成另外的一些ConfigurationClass,接下来就利用reader来进一步解析ConfigurationClass
如果ConfigurationClass是通过@Import注解导入进来的,则把这个类生成一个BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition
如果ConfigurationClass中存在一些BeanMethod,也就是定义了一些@Bean,那么则解析这些@Bean,并生成对应的BeanDefinition,并注册
如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文件,得到并注册BeanDefinition
如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应的registerBeanDefinitions进行BeanDefinition的注册

在这里插入图片描述
在这里插入图片描述

3、postProcessBeanFactory方法

// org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClasses
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
	StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
	Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
	for (String beanName : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
		Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
		AnnotationMetadata annotationMetadata = null;
		MethodMetadata methodMetadata = null;
		if (beanDef instanceof AnnotatedBeanDefinition) {
			AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
			annotationMetadata = annotatedBeanDefinition.getMetadata();
			methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
		}
		if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
			// Configuration class (full or lite) or a configuration-derived @Bean method
			// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
			// or component class without @Bean methods.
			AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
			if (!abd.hasBeanClass()) {
				boolean liteConfigurationCandidateWithoutBeanMethods =
						(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
							annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
				if (!liteConfigurationCandidateWithoutBeanMethods) {
					try {
						abd.resolveBeanClass(this.beanClassLoader);
					}
					catch (Throwable ex) {
						throw new IllegalStateException(
								"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
					}
				}
			}
		}
		if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
			if (!(beanDef instanceof AbstractBeanDefinition)) {
				throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
						beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
			}
			else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
				logger.info("Cannot enhance @Configuration bean definition '" + beanName +
						"' since its singleton instance has been created too early. The typical cause " +
						"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
						"return type: Consider declaring such methods as 'static'.");
			}
			configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
		}
	}
	if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
		// nothing to enhance -> return immediately
		enhanceConfigClasses.end();
		return;
	}

	// 代理
	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
		AbstractBeanDefinition beanDef = entry.getValue();
		// If a @Configuration class gets proxied, always proxy the target class
		beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
		// Set enhanced subclass of the user-specified bean class
		Class<?> configClass = beanDef.getBeanClass();
		Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
		if (configClass != enhancedClass) {
			if (logger.isTraceEnabled()) {
				logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
						"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
			}
			beanDef.setBeanClass(enhancedClass);
		}
	}
	enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

配置类,如果是full的配置类,会生成一个代理类。

我们看enhancer.enhance生成的代理对象:
在这里插入图片描述
在这里插入图片描述
我们看一下BeanMethodInterceptor的代理方法:

// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
			MethodProxy cglibMethodProxy) throws Throwable {

	ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
	String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

	// Determine whether this bean is a scoped-proxy
	if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
		String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
		if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
			beanName = scopedBeanName;
		}
	}

	// To handle the case of an inter-bean method reference, we must explicitly check the
	// container for already cached instances.

	// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
	// proxy that intercepts calls to getObject() and returns any cached bean instance.
	// This ensures that the semantics of calling a FactoryBean from within @Bean methods
	// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
	if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
			factoryContainsBean(beanFactory, beanName)) {
		Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
		if (factoryBean instanceof ScopedProxyFactoryBean) {
			// Scoped proxy factory beans are a special case and should not be further proxied
		}
		else {
			// It is a candidate FactoryBean - go ahead with enhancement
			return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
		}
	}
	// 代理类执行某个方法的时候,会判断正在执行的方法是不是正在创建Bean的方法
	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		// The factory is calling the bean method in order to instantiate and register the bean
		// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
		// create the bean instance.
		if (logger.isInfoEnabled() &&
				BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
			logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
							"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
							"result in a failure to process annotations such as @Autowired, " +
							"@Resource and @PostConstruct within the method's declaring " +
							"@Configuration class. Add the 'static' modifier to this method to avoid " +
							"these container lifecycle issues; see @Bean javadoc for complete details.",
					beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
		}
		// 直接执行该方法
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}

	return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

这就意味着,同一个代理配置类中调用@Bean的方法,相当于获取了同一个Bean。
在这里插入图片描述

后记

JFR:
https://zhuanlan.zhihu.com/p/122247741

@LookUp注解

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

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

相关文章

1046: 链栈基本操作的实现

解法&#xff1a;学习版看刚开始的文章 #include<iostream> #include<stack> using namespace std; #define int long long signed main() {int n, a, k;stack<int> sk;cin >> n;while (n--) {cin >> a;sk.push(a);}cin >> k;if (k >…

YOLOV5检测界面搭建+bug解决

目录 一、环境搭建 二、界面运行bug解决 三、界面 先给出Github链接&#xff1a;https://github.com/Javacr/PyQt5-YOLOv5 大佬链接&#xff1a;大佬 一、环境搭建 下载完项目后&#xff0c;需要配置环境&#xff1a; conda create -n yolov5_pyqt5 python3.8 conda act…

【洛谷 P8802】[蓝桥杯 2022 国 B] 出差 题解(带权无向图+单源最短路+Dijkstra算法+链式前向星+最小堆)

[蓝桥杯 2022 国 B] 出差 题目描述 A \mathrm{A} A 国有 N N N 个城市&#xff0c;编号为 1 … N 1 \ldots N 1…N 小明是编号为 1 1 1 的城市中一家公司的员工&#xff0c;今天突然接到了上级通知需要去编号为 N N N 的城市出差。 由于疫情原因&#xff0c;很多直达的交…

【Java开发指南 | 第六篇】Java成员变量(实例变量)、 类变量(静态变量)

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 成员变量&#xff08;实例变量&#xff09;类变量&#xff08;静态变量&#xff09;定义方式静态变量的使用场景 成员变量&#xff08;实例变量&#xff09; 成员变量声明在一个类中&#xff0c;但在方法、构造…

SpringCloud之LoadBalancer自定义负载均衡算法,基于nacos权重

LoadBalancer基于Nacos权重自定义负载算法 ReactorLoadBalancer接口&#xff0c;实现自定义负载算法需要实现该接口&#xff0c;并实现choose逻辑&#xff0c;选取对应的节点 public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {Mono<…

(一)基于IDEA的JAVA基础16(end)

二维数组 二维数组就是数组里面再放一个数组 语法: <数据类型> [] [] 数组名&#xff1b; 或: <数据类型> 数组名 [] []&#xff1b; 比如这里有5个单位&#xff0c;每个单位员工有20个&#xff0c;他们都在忙几个相同的项目&#xff0c;现在要对某项项目进行操…

js 写 视频轮播

html代码 <div class"test_box"> <div class"test"> <a href"#"> <div class"test_a_box"> <div class"test_a_mask"></div> <div class"test_a_layer"> <div cla…

2024蓝桥A组D题

团建 问题描述格式输入格式输出样例输入样例输出评测用例规模与约定解析参考程序难度等级 问题描述 格式输入 输入的第一行包含两个正整数n,m&#xff0c;用一个空格分隔。 第二行包含n个正整数c1,c2, ,cn&#xff0c;相邻整数之间使用一个空格分隔&#xff0c; 其中ci表示第一…

idea新建一个springboot项目

本文分为几个部分&#xff0c; 首先是在idea中新建项目&#xff0c; 然后是配置 项目的目录&#xff08;新建controller、service、dao等&#xff09;&#xff0c; 然后是自定义的一些工具类&#xff08;比如启动后打印地址等&#xff09;。 1.、创建篇 新建项目&#xff0…

毕设选51还是stm32?51太简单?

如果你更倾向于挑战和深入学习&#xff0c;STM32可能是更好的选择。如果你希望更专注于底层硬件原理&#xff0c;51可能更适合。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff…

bestvike 资料 --Spring Boot 2.5.0

Spring Boot 2.5.0 SSM环境搭建 springspringmvcmybatisspring springmvc mybatis # 项目 - 需求分析 概要设计(库表设计) 详细设计(验证库表正确性) 编码(环境搭建业务代码) 测试 部署上线# 员工添加 查询所有功能 SSM - 库表 库: ssm 数据库:mysql 表: id na…

【教程】将Vue项目打包为exe项目的教程-我的第一个原生Vue项目

文章目录 前言项目介绍正文&#xff1a;Vue打包exe过程及注意事项1. &#xff08;重要&#xff09;进入我们自己的项目&#xff0c;修改公共路径为相对路径2. &#xff08;重要&#xff09;关于VueRouter的必要修改3. 前端打包4. 拉取electron-quick-start项目5. 修改配置文件6…

【高阶数据结构】哈希表 {哈希函数和哈希冲突;哈希冲突的解决方案:开放地址法,拉链法;红黑树结构 VS 哈希结构}

一、哈希表的概念 顺序结构以及平衡树 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系。因此在查找一个元素时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff1b;平衡树中为树的高度&#xff0c;即O(log_2 N)&#xf…

【python】项目实战

启动一个项目对于新手都是不容易的事情 在哪 对于Windows平台&#xff0c;打开cmd 使用命令py -0p 【其中0是零】 显示已安装的 python 版本且带路径的列表 切换python3命令 在Windows下&#xff0c;可以使用cmd下使用mklink命令创建“软链接”更好一些。 例如&#xf…

weblogic oracle数据源配置

在weblogic console中配置jdbc oracle数据源 1. base_domain->Service->DataSources 在Summary of JDBC Data Sources中&#xff0c;点击New, 选择【Generic Data Source】通用数据源。 2. 设置数据源Name和JNDI name 注&#xff1a;设置的JNDI Name是Java AP中连接…

正则匹配密码 - 使用正向先行断言

代码 import redef password_is_ok(password: str) -> bool:"""1. 密码只能是字母数字组合2. 密码必须包含大小写字母以及数字"""result re.findall(^(?.*\d)(?.*[a-z])(?.*[A-Z])[0-9a-zA-Z]{8,16}$, password)return True if result e…

HCIP的学习(9)

OSPF的接口网络类型 ​ OSPF的接口在某种网络类型下的工作方式。 网络类型OSPF接口的工作方式BMABroadcast&#xff1b;可以建立多个邻居关系。需要进行DR选举。hello 10S&#xff1b;dead 40S。P2PP2P&#xff1b;只能建立一个邻居关系&#xff0c;不需要进行DR选举。Hello …

语音智能客服机器人有什么优势?ai机器人部署

人工智能技术的进步&#xff0c;在不断的革新我们的工作和生活&#xff0c;同时&#xff0c;拥有人工智能技术的语音智能客服机器人在销售行业的工作熟悉程度也越来越好&#xff0c;那语音智能客服机器人有什么优势&#xff1f;我们一起来看看。 1、ASR语音文本转换 客户可通过…

量子城域网系列(四):几种典型的量子密钥分发网络组网方案

通过之前的文章&#xff0c;我们对点对点的量子保密通信网络有了直观的认识&#xff0c;也知道了量子保密通信系统就是利用量子密钥分发产生的无条件安全量子密钥作为系统安全性保证。所以在实际应用中&#xff0c;一个通信网络如果想实现量子加密&#xff0c;就需要建设量子密…

计算机网络3——数据链路层1

文章目录 一、介绍1、基础2、内容 二、数据链路层的几个共同问题1、数据链路和帧2、三个基本问题1&#xff09;封装成帧2&#xff09;透明传输3&#xff09;差错检测 三、点对点协议 PPP1、PPP协议的特点1&#xff09;PPP 协议应满足的需求2&#xff09;PPP 协议的组成 2、PPP协…