Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor
序言
前几篇文章详细介绍了Spring中实例化Bean的各种方式,其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。
创建对象
这里再来简单回顾一下对象的创建,不知道大家有没有这样一个疑问,为什么创建对象之前要获取实例策略的?意义在哪?
因为我们在createBeanInstance()
中调用instantiateBean()
方法进行类的实例化创建时,会有很多种选择 。根据构造器、工厂方法、参数…等等,而其中一部分是采用Cglib动态代理的方式实例化,其中一部分就是普通的Simple实例化。
SimpleInstantiationStrategy
而我们看到SimpleInstantiationStrategy
类中方法就会方法,类中共有3个instantiate()
同名方法,而每个方法传递的参数也大不一样,根据参数就可判断出是根据不同的条件来创建对象(构造器、工厂方法…)
public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic 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");}@Overridepublic 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");}@Overridepublic Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,@Nullable Object factoryBean, final Method factoryMethod, Object... args) {// 省略方法体}
}
而继承CglibSubclassingInstantiationStrategy
了SimpleInstantiationStrategy
,因为在Simple类中已经对instantiate()
进行了三种不同的实现,所以在Cglib中没对instantiate()
做额外处理,而是实现了instantiateWithMethodInjection
方法。
但底层调用的也是instantiate()
,并根据构造器来判断具体的实现方式。
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {@Overrideprotected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {return instantiateWithMethodInjection(bd, beanName, owner, null);}@Overrideprotected 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;@PostConstructpublic void init(){System.out.println("执行init方法");}@PreDestroypublic 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对象的classClass<?> beanType = instanceWrapper.getWrappedClass();//如果不是NullBean类型,则修改目标类型if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.//允许postProcessor修改合并的BeanDefinitionsynchronized (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.class
和PreDestroy.class
设置到属性中。
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessorimplements 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);}}
方法首先会调用父类中InitDestroyAnnotationBeanPostProcessor
的postProcessMergedBeanDefinition
方法。
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
相关文章:

Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor
序言 前几篇文章详细介绍了Spring中实例化Bean的各种方式,其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。 创建对象 这里再来简单回顾一下对象的创建,不知道大家有没有这样一个疑…...

labview技巧——AMC框架安装
AMC工具包的核心概念是队列,队列是一种先进先出(FIFO,First In First Out)的数据结构,适用于处理并发和异步任务。在LabVIEW中,队列可以用于在不同VI之间传递数据,确保消息的有序处理࿰…...

解锁分布式云多集群统一监控的云上最佳实践
作者:在峰 引言 在当今数字化转型加速的时代,随着混合云、多云多集群环境等技术被众多企业广泛应用,分布式云架构已成为众多企业和组织推动业务创新、实现弹性扩展的首选,分布式云容器平台 ACK One(Distributed Clou…...

学会拥抱Python六剑客,提高编程效率
在Python语言中,有六个强大的工具,它们被称为"Python六剑客"。而Python六剑客指的是Python中常用的六种功能强大且灵活的工具,它们分别是“切片(Slicing),推导列表(List Comprehensio…...

mysql 根据当前时间筛选某个时间范围内的数据
1.根据天数筛选 SELECT * FROM coupons WHERE NOW() BETWEEN start_time AND end_time; 在这个查询中,NOW()函数返回当前的日期和时间。BETWEEN操作符用于检查NOW()返回的当前时间是否在start_time和end_time之间(包括这两个时间)。 注意&a…...

Linux 常用指令详解
Linux 是一个强大而灵活的操作系统,掌握常用的 Linux 指令是使用和管理 Linux 系统的基础。本文将介绍一些常用的 Linux 指令,并附上 Vim 和 g 的常用指令说明,帮助你更好地进行开发和操作。 1. 基本文件操作指令 1.1 显示目录内容 ls常用…...

【简单讲解下npm常用命令】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

Header Location重定向机制解析与应用
Header Location重定向机制解析与应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨HTTP中的Header Location重定向机制,以及在…...

硅纪元AI应用推荐 | 国产创作引擎即梦AI助力创作者探索创作新境界
“硅纪元AI应用推荐”栏目,为您精选最新、最实用的人工智能应用,无论您是AI发烧友还是新手,都能在这里找到提升生活和工作的利器。与我们一起探索AI的无限可能,开启智慧新时代! 在人工智能快速发展的今天,各…...

使用TableGeneration生成已标注的表格数据用于表格识别
利用 TableGeneration 生成多样化表格数据 TableGeneration 简介环境准备chrome浏览器(Linux下推荐)火狐浏览器(Mac下推荐) 生成表格生成表格 参数说明结论 在数据生成和处理领域,表格数据的生成是一个常见需求,尤其是在机器学习和数据分析领域。今天&am…...

赛目科技三度递表:净利率及资产回报率不断下滑,经营成本越来越高
《港湾商业观察》施子夫 5月29日,北京赛目科技股份有限公司(以下简称,赛目科技)第三次递表港交所,公司拟主板上市,独家保荐机构为光银国际。 公开信息显示,赛目科技此前曾于2022年12月&#x…...

【QT】概述|对象树模型|两种控件模式|信号和槽|lambda
目录 什么是QT 特点 QT程序 main函数 QT按钮 纯代码模式 图形化模式 对象树模型 信号和槽 连接与断开 自动连接 断开连接 信号的发射 lambda表达式 基本语法 捕获列表 Lambda表达式用于信号与槽的连接 例如 什么是QT Qt是一个跨平台的C图形用户界面应用…...

Java中的安全编码实践与防御技巧
Java中的安全编码实践与防御技巧 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨Java中的安全编码实践与防御技巧,这对于开发人员来说…...

linux 常用的命令、文件路径、其他工具或软件包
命令 sudo apt dist-upgrade 解决显示 暂不升级、未被升级dd if/dev/zero of./rootfs.img bs1G count6 制作一个 6G 的空白镜像。bs 是块字节数,count 是 bs 的个数。dd if./rootfs.img of/dev/sdc2 bs512 烧录 rootfs.img 镜像到 /dev/sdc2。bs 是 512 个字节&…...

00 如何根据规律在变化中求发展?
你好,我是周大壮。目前,我已在搜索推荐等算法技术领域从事研发近 10 年,做过诸多流量分发领域的算法技术工作。 如今任 58 同城的算法架构师、技术委员会人工智能分会委员、58 本地服务事业群算法策略部负责人,我主要负责 58 集团…...

UUID和自增ID做主键到底哪个好?
UUID(通用唯一识别码) 优点: 全局唯一性:由不同算法生成,确保全球范围内的唯一性,避免主键冲突。不可预测性:随机生成,难以被猜测,适合保密性应用。分布式应用࿱…...

FW SystemUI Keyguard解析(二)
文章目录 CTS之Keyguard Menu事件处理 CTS之Keyguard Menu事件处理 事件触发点: NotificationShadeWindowViewController.dispatchKeyEvent 设置setInteractionEventHandler回调之后通过NotificationShadeWindowView 触发 调用到return mService.onMenuPressed(); public cla…...

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

MySQL:保护数据库
保护数据库 1. 用户1.1 创建用户1.2 查看用户1.3 删除用户1.4 修改密码 2. 权限2.1 授予权限2.2 查看权限2.3 撤销权限 之前都是介绍本地数据库而你自己就是数据库的唯一用户,所以不必考虑安全问题。但实际业务中数据库大多放在服务器里,你必须妥善处理好…...

不是大厂云用不起,而是五洛云更有性价比
明月代维的一个客户的大厂云境外云服务器再有几天就到期了,续费提醒那是提前一周准时到来,但是看到客户发来的续费价格截图,我是真的没忍住。这不就是在杀熟吗?就这配置续费竟然如此昂贵?说实话这个客户的服务器代维是…...

C++初学者指南-3.自定义类型(第一部分)-异常
C初学者指南-3.自定义类型(第一部分)-异常 文章目录 C初学者指南-3.自定义类型(第一部分)-异常简介什么是异常?第一个示例用途:报告违反规则的行为异常的替代方案标准库异常处理 问题和保证资源泄露使用 RAII 避免内存泄漏!析构函数:不要让异…...

学会python——用python编写一个电子时钟(python实例十七)
目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.电子时钟程序 3.1 代码构思 3.2代码实例 3.3运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性…...

elementui中@click短时间内多次触发,@click重复点击,做不允许重复点击处理
click快速点击,发生多次触发 2.代码示例: //html<el-button :loading"submitLoading" type"primary" click"submitForm">确 定</el-button>data() {return {submitLoading:false,}}//方法/** 提交按钮 */sub…...

助力游戏实现应用内运营闭环,融云游戏社交方案升级!
通信能力在所有应用场景都是必备组件,这源于社交属性带给应用的增长神话。 在游戏场景,玩家从少数核心向大众用户泛化扩展的过程,就是游戏深度融合社交能力的过程。 从单机到联机,游戏乐趣的升级 1996 年,游戏界顶流…...

守护创新之魂:源代码防泄漏的终极策略
在信息化快速发展的今天,企业的核心机密数据,尤其是源代码,成为了企业竞争力的关键所在。然而,源代码的泄露风险也随之增加,给企业的安全和发展带来了巨大威胁。在这样的背景下,SDC沙盒作为一种创新的源代码…...

Halcon 基于分水岭的目标分割
一 分水岭 1 分水岭介绍 传统的分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是地质学上的拓扑地貌,图像中每一像素的灰度值表示该点的海拔高度,每一个局部极小值及其周边区域称为集水盆地&…...

PHP 面向对象编程(OOP)入门指南
面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,通过使用对象来设计和组织代码。PHP作为一种广泛使用的服务器端脚本语言,支持面向对象编程。本文将介绍PHP面向对象编程的基本概念和用法࿰…...

Django学习第三天
python manage.py runserver 使用以上的命令启动项目 实现新建用户数据功能 views.py文件代码 from django.shortcuts import render, redirect from app01 import models# Create your views here. def depart_list(request):""" 部门列表 ""&qu…...

Vue3实现点击按钮实现文字变色
1.动态样式实现 1.1核心代码解释: class"power-station-perspective-item-text": 为这个 span 元素添加了一个 CSS 类,以便对其样式进行定义。 click"clickItem(item.id)": 这是一个 Vue 事件绑定。当用户点…...

深入理解Vue生命周期钩子函数
深入理解Vue生命周期钩子函数 Vue.js 是一款流行的前端框架,通过其强大的响应式数据绑定和组件化的开发方式,使得前端开发变得更加简单和高效。在Vue应用中,每个组件都有其生命周期,这些生命周期钩子函数允许开发者在不同阶段执行…...