Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor

序言

前几篇文章详细介绍了Spring中实例化Bean的各种方式,其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。

创建对象
这里再来简单回顾一下对象的创建,不知道大家有没有这样一个疑问,为什么创建对象之前要获取实例策略的?意义在哪?
在这里插入图片描述
因为我们在createBeanInstance()中调用instantiateBean()方法进行类的实例化创建时,会有很多种选择 。根据构造器、工厂方法、参数…等等,而其中一部分是采用Cglib动态代理的方式实例化,其中一部分就是普通的Simple实例化。
在这里插入图片描述

SimpleInstantiationStrategy
而我们看到SimpleInstantiationStrategy类中方法就会方法,类中共有3个instantiate()同名方法,而每个方法传递的参数也大不一样,根据参数就可判断出是根据不同的条件来创建对象(构造器、工厂方法…)

public class SimpleInstantiationStrategy implements InstantiationStrategy {

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// 省略方法逻辑
	}
	
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
	}

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			final Constructor<?> ctor, Object... args) {
		// 省略方法逻辑
	}

	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName,
			BeanFactory owner, @Nullable Constructor<?> ctor, Object... args) {

		throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
	}

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
		// 省略方法体
	}
}

而继承CglibSubclassingInstantiationStrategySimpleInstantiationStrategy,因为在Simple类中已经对instantiate()进行了三种不同的实现,所以在Cglib中没对instantiate()做额外处理,而是实现了instantiateWithMethodInjection方法。
但底层调用的也是instantiate(),并根据构造器来判断具体的实现方式。

public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {

	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		return instantiateWithMethodInjection(bd, beanName, owner, null);
	}

	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Constructor<?> ctor, Object... args) {

		// Must generate CGLIB subclass...
		return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
	}
	
	// 省略部分源码
	public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
		//根据beanDefinition创建一个动态生成的子类
		Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
		Object instance;
		// 如果构造器等于空,那么直接通过反射来实例化对象
		if (ctor == null) {
			instance = BeanUtils.instantiateClass(subclass);
		}
		else {
			try {
				// 通过cglib对象来根据参数类型获取对应的构造器
				Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
				// 通过构造器来获取对象
				instance = enhancedSubclassConstructor.newInstance(args);
			}
		// 省略部分源码
	}
}	

所以Spring中获取实例话策略后,共5种创建对象的方式。并且Spring中对象的创建也并不都是采用Cglib动态代理。
在这里插入图片描述
回顾完了对象的创建,我们顺着代码的逻辑继续向下执行。
现在对象创建了,但是我们还不知道对象的初始化(init)和销毁(destroy)方法是什么。接下来就是对这两个方法做处理。

测试类

Person类中省略了get、set和构造器方法。
而为什么不用@Init注解来表示初始化方法?
因为Spring中并没有提供,下面的两个注解是Java提供的元注解,优先于@Init方法执行。

public class Person {

	private String name;
	private int age;

	@PostConstruct
	public void init(){
		System.out.println("执行init方法");
	}
	@PreDestroy
	public void destroy(){
		System.out.println("执行destroy方法");
	}
}

mergePostProcessor.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="org.springframework.mergePostProcessor"></context:component-scan>
	<bean id="person" class="org.springframework.mergePostProcessor.Person">
		<property name="name" value="张三"></property>
		<property name="age" value="18"></property>
	</bean>
</beans>

main

public class TestMergePostProcessor {
	public static void main(String[] args) {

		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("mergePostProcessor.xml");
		Person person = ac.getBean("person", Person.class);
		ac.close();
	}
}

mergeBeanDefinitionPostProcessor

让我们把视线拉回到doCreateBean()中。 此时我们已经通过createBeanInstance()完成了对象的实例化操作。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		//这个beanWrapper是用来持有创建出来的bean对象的
		BeanWrapper instanceWrapper = null;

		//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息
		if (mbd.isSingleton()) {
			// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 没有就创建实例
		if (instanceWrapper == null) {
			// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//从包装类wrapped中获取原始的实例
		Object bean = instanceWrapper.getWrappedInstance();
		//获取具体bean对象的class
		Class<?> beanType = instanceWrapper.getWrappedClass();
		//如果不是NullBean类型,则修改目标类型
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		//允许postProcessor修改合并的BeanDefinition
		synchronized (mbd.postProcessingLock) {
			// 如果没有执行过下面方法。
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException;
				}
				mbd.postProcessed = true;
			}
		}
		// 省略部分源码
	}	

获取BeanPostProcess,并找到MergedBeanDefinitionPostProcessor类型的执行postProcessMergedBeanDefinition方法。

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof MergedBeanDefinitionPostProcessor) {
				MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
				bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
			}
		}
	}

先来看眼postProcessMergedBeanDefinition的继承关系。
在这里插入图片描述
postProcessMergedBeanDefinition()方法的实现有很多,但是其中都有一个特点,就是都是AnnotationBeanPostProcessor后缀,所以都是对注解的处理。
而当我们Person对象加载、解析<context:component-scan>标签时,就会将CommonAnnotationBeanPostProcessor注入到工厂中。所以在上面getBeanPostProcessors()调用时,会获取到CommonAnnotationBeanPostProcessor并执行postProcessMergedBeanDefinition
在这里插入图片描述
关于<context:component-scan>标签解析时如何进行组件的注册可看这篇帖子context: component-scan标签如何扫描、加载Bean。

CommonAnnotationBeanPostProcessor
当我们CommonAnnotationBeanPostProcessor实例创建时,构造方法中会将PostConstruct.classPreDestroy.class设置到属性中。

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
		implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
	
	public CommonAnnotationBeanPostProcessor() {
		setOrder(Ordered.LOWEST_PRECEDENCE - 3);
		setInitAnnotationType(PostConstruct.class);
		setDestroyAnnotationType(PreDestroy.class);
		ignoreResourceType("javax.xml.ws.WebServiceContext");
	}
	
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
			//处理@PostConstruct 和 @PreDestroy 注解
			super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
			InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
			metadata.checkConfigMembers(beanDefinition);
		}
	}	

方法首先会调用父类中InitDestroyAnnotationBeanPostProcessorpostProcessMergedBeanDefinition方法。

postProcessMergedBeanDefinition
获取生命周期元数据信息并保存

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		//获取生命周期元数据信息并保存
		LifecycleMetadata metadata = findLifecycleMetadata(beanType);
		metadata.checkConfigMembers(beanDefinition);
	}

findLifecycleMetadata
依然是先从缓存中获取,获取不到则构建LifecycleMetadata对象并放到缓存中。

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
		//如果生命周期元数据缓存为空,则直接构建生命周期元数据 (默认创建了一个ConCurrentHashMap)
		if (this.lifecycleMetadataCache == null) {
			// Happens after deserialization, during destruction...
			return buildLifecycleMetadata(clazz);
		}
		// Quick check on the concurrent map first, with minimal locking.
		//快速检查生命周期元数据缓存,如果没有,则构建生命周期元数据
		LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
		//缓存中不存在
		if (metadata == null) {
			//双层锁,防止多线程重复执行
			synchronized (this.lifecycleMetadataCache) {
				//再次从缓存中获取
				metadata = this.lifecycleMetadataCache.get(clazz);
				if (metadata == null) {
					//构建生命周期元数据
					metadata = buildLifecycleMetadata(clazz);
					//将构建好的数据放入缓存中
					this.lifecycleMetadataCache.put(clazz, metadata);
				}
				return metadata;
			}
		}
		return metadata;
	}

在这里插入图片描述
buildLifecycleMetadata
将 init 方法和 destroy 方法分类,如果父类中也包含注解,则循环处理。
因为会优先执行父类初始化方法, 所以 init 放入集合时,会放在 index = 0 的位置。

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
		//是否包含@PostConstruct注解和@PreDestroy注解
		if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
			return this.emptyLifecycleMetadata;
		}
		//声明初始化方法集合
		List<LifecycleElement> initMethods = new ArrayList<>();
		//声明销毁方法集合
		List<LifecycleElement> destroyMethods = new ArrayList<>();
		//目标类型
		Class<?> targetClass = clazz;

		do {
			//保存当前正在处理的方法
			final List<LifecycleElement> currInitMethods = new ArrayList<>();
			final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

			// 反射获取当前类中的所有方法并依次对其调用第二个参数的lambda表达式
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				//当前方法包含initAnnotationType注解时(@PostConstruct)
				if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
					//封装成LifecycleElement对象,添加到currInitMethods集合中
					LifecycleElement element = new LifecycleElement(method);
					currInitMethods.add(element);
	
				}
				// 当前方法包含destroyAnnotationType注解时(@PreDestroy)
				if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
					//封装成LifecycleElement对象,添加到currDestroyMethods集合中
					currDestroyMethods.add(new LifecycleElement(method));
		
				}
			});
			//每次都将currInitMethods集合中的元素添加到initMethods集合最前面
			// 因为可能父类中也包含@PostConstruct注解的方法,所以需要先执行父类的方法
			initMethods.addAll(0, currInitMethods);
			//放入destroyMethod的总集合中
			destroyMethods.addAll(currDestroyMethods);
			//获取父类
			targetClass = targetClass.getSuperclass();
		}
		//如果父类不是Object.class,则继续循环
		while (targetClass != null && targetClass != Object.class);
		//如果集合为空,则返回一个空的LifecycleMetadata对象,否则封装并返回一个LifecycleMetadata对象
		return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
				new LifecycleMetadata(clazz, initMethods, destroyMethods));
	}

在这里插入图片描述
而当我们处理完后会进行检查,并放入beanDefinition中,等待执行。

public void checkConfigMembers(RootBeanDefinition beanDefinition) {
			Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
			for (LifecycleElement element : this.initMethods) {
				String methodIdentifier = element.getIdentifier();
				if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
					// 注册初始化调用方法
					beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
					checkedInitMethods.add(element);

				}
			}
			Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
			for (LifecycleElement element : this.destroyMethods) {
				String methodIdentifier = element.getIdentifier();
				if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
					// 注册销毁调用方法
					beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
					checkedDestroyMethods.add(element);

				}
			}
			this.checkedInitMethods = checkedInitMethods;
			this.checkedDestroyMethods = checkedDestroyMethods;
		}

执行完成后,beanDefinition中也有了待执行的初始化方法和销毁方法。
在这里插入图片描述
因为整个Bean的实例化、加载过程只有BeanDefinition是伴随始终的,所以处理完之后要设置到BD中,也正好印证了方法上的那个注释:允许postProcessor修改合并的BeanDefinition

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL之备份与恢复(二)

备份与恢复 定义恢复需求 如果一切正常&#xff0c;那么永远也不需要考虑恢复。但是&#xff0c;一旦需要恢复&#xff0c;只有世界上最好的备份系统是没用的&#xff0c;还需要一个强大的恢复系统。 不幸的是&#xff0c;让备份系统平滑工作比构造良好的恢复过程和工具更容易…

优先级队列(堆)学的好,头发掉的少(Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

使用 Ollama 时遇到的问题

题意&#xff1a; ImportError: cannot import name Ollama from llama_index.llms (unknown location) - installing dependencies does not solve the problem Python 无法从 llama_index.llms 模块中导入名为 Ollama 的类或函数 问题背景&#xff1a; I want to learn LL…

七大排序算法的深入浅出(java篇)

&#x1f341; 个人主页&#xff1a;爱编程的Tom&#x1f4ab; 本篇博文收录专栏&#xff1a;Java专栏&#x1f449; 目前其它专栏&#xff1a;c系列小游戏 c语言系列--万物的开始_ 等等 &#x1f389; 欢迎 &#x1f44d;点赞✍评论⭐收藏&#x1f496;三连支…

springboot 整合 mybatis-plus

一.前言 1. mybatis-plus是什么 mybatis-plus是一个对mybati框架的拓展框架&#xff0c;它在mybatis框架基础上做了许多的增强&#xff0c;帮助我们快速的进行代码开发。目前企业开发中&#xff0c;使用mybati的项目基本会选择使用mybatis-plus来提升开发效率。 2.官网地址&…

Study--Oracle-06-Oracler网络管理

一、ORACLE的监听管理 1、ORACLE网络监听配置文件 cd /u01/app/oracle/product/12.2.0/db_1/network/admin 2、在Oracle数据库中&#xff0c;监听器&#xff08;Listener&#xff09;是一个独立的进程&#xff0c;它监听数据库服务器上的特定端口上的网络连接请求&#xff0c…

四十篇:内存巨擘对决:Redis与Memcached的深度剖析与多维对比

内存巨擘对决&#xff1a;Redis与Memcached的深度剖析与多维对比 1. 引言 在现代的系统架构中&#xff0c;内存数据库已经成为了信息处理的核心技术之一。这类数据库系统的高效性主要来源于其对数据的即时访问能力&#xff0c;这是因为数据直接存储在RAM中&#xff0c;而非传统…

p2p、分布式,区块链笔记: 通过libp2p的Kademlia网络协议实现kv-store

Kademlia 网络协议 Kademlia 是一种分布式哈希表协议和算法&#xff0c;用于构建去中心化的对等网络&#xff0c;核心思想是通过分布式的网络结构来实现高效的数据查找和存储。在这个学习项目里&#xff0c;Kademlia 作为 libp2p 中的 NetworkBehaviour的组成。 以下这些函数或…

AI 会淘汰程序员吗?

前言 前些日子看过一篇文章&#xff0c;说国外一位拥有 19 年编码经验、会 100% 手写代码的程序员被企业解雇了&#xff0c;因为他的竞争对手&#xff0c;一位仅有 4 年经验、却善于使用 Copilot、GPT-4 的后辈&#xff0c;生产力比他更高&#xff0c;成本比他更低&#xff0c…

基于java+springboot+vue实现的家政服务平台(文末源码+Lw)299

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本家政服务平台就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&a…

2. Python+Playwright playwright的API

Playwright支持同步和异步两种API&#xff0c;使用异步API需要导入asyncio库&#xff0c;它是一个可以用来实现Python协程的库&#xff0c;更详细介绍可参考Python协程 。我们可以根据自己的偏好选择适合的模式。 同步与异步模式原理 同步操作方式&#xff1a;在代码执行时&am…

SpringBoot 整合 Minio 实现文件切片极速上传技术

Centos7安装Minio 创建目标文件夹 mkdir minio使用docker查看目标镜像状况 大家需要注意&#xff0c;此处我们首先需要安装docker&#xff0c;对于相关安装教程&#xff0c;大家可以查看我之前的文章&#xff0c;按部就班就可以&#xff0c;此处不再赘述&#xff01;&#x…

学习和发展人工智能:新兴趋势和成功秘诀

人工智能(AI)继续吸引组织&#xff0c;因为它似乎无穷无尽地提高生产力和业务成果。在本博客中&#xff0c;了解学习和发展(L&D)部门如何利用人工智能改进流程&#xff0c;简化工作流程&#xff1f; 学习与发展(L&D)部门领导开始探索如何提高和支持人工智能能力的劳动…

Linux Swap机制关键点分析

1. page被swap出去之后,再次缺页是怎么找到找个换出的页面? 正常内存的页面是通过pte映射找到page的,swap出去的page有其特殊的方式:swap的页面page->private字段保存的是:swap_entry_t通过swap_entry_t就能找到该页面的扇区号sector_t,拿到扇区号就可以从块设备中读…

充电宝哪个牌子比较好用?好用的充电宝推荐!

在如今这个电子设备不离手的时代&#xff0c;充电宝已经成为了我们生活中的必备好物。但面对市面上琳琅满目的充电宝品牌和产品&#xff0c;相信很多朋友都曾陷入过纠结&#xff1a;充电宝哪个牌子比较好用呢&#xff1f;为了解决大家的困惑&#xff0c;经过我精心的筛选和试用…

8.x86游戏实战-OD详解

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.x86游戏实战-C实现跨进程读写-跨进程写内存 工具下载&#xff1a;下载 OllyI…

【信即是功夫】人皆有良知在心中

良知就是做人、做事的准则&#xff0c;良知就是天理&#xff1b;实实在在地自信 每个人心中都有一个圣人&#xff0c;只因自己不能真的相信&#xff0c;把这个圣人埋没了 良知在每个人心中&#xff0c;无论你如何做&#xff0c;也无法泯灭它。即使身为盗贼的人&#xff0c;他…

【LeetCode的使用方法】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 🔮LeetCode的使用方法 🔮LeetCode 是一个在线编程平台,广泛…

掌握Go语言邮件发送:net/smtp实用教程与最佳实践

掌握Go语言邮件发送&#xff1a;net/smtp实用教程与最佳实践 概述基本配置与初始化导入net/smtp包设置SMTP服务器基本信息创建SMTP客户端实例身份验证 发送简单文本邮件配置发件人信息构建邮件头部信息编写邮件正文使用SendMail方法发送邮件示例代码 发送带附件的邮件邮件多部分…

STM32之五:TIM定时器(2-通用定时器)

目录 通用定时器&#xff08;TIM2~5&#xff09;框图 1、 输入时钟源选择 2、 时基单元 3 、输入捕获&#xff1a;&#xff08;IC—Input Capture&#xff09; 3.1 输入捕获通道框图&#xff08;TI1为例&#xff09; 3.1.1 滤波器&#xff1a; 3.1.2 边沿检测器&#xf…