Spring扩展点系列-SmartInstantiationAwareBeanPostProcessor

news2024/9/20 19:02:42

文章目录

    • 简介
    • 源码分析
    • 示例

简介

spring容器中Bean的生命周期内所有可扩展的点的调用顺序
扩展接口 实现接口
ApplicationContextlnitializer initialize
AbstractApplicationContext refreshe
BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry
BeanDefinitionRegistryPostProcessor postProcessBeanFactory
BeanFactoryPostProcessor postProcessBeanFactory
instantiationAwareBeanPostProcessor postProcessBeforelnstantiation
SmartlnstantiationAwareBeanPostProcessor determineCandidateConstructors
MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition
InstantiationAwareBeanPostProcessor postProcessAfterlnstantiation
SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference
BeanNameAware setBeanName
BeanFactoryAware postProcessPropertyValues
ApplicationContextAwareProcessor invokeAwarelnterfaces
InstantiationAwareBeanPostProcessor postProcessBeforelnstantiation
@PostConstruct
InitializingBean afterPropertiesSet
FactoryBean getobject
SmartlnitializingSingleton afterSingletonslnstantiated
CommandLineRunner run
DisposableBeandestroy

提到SmartInstantiationAwareBeanPostProcessor,这里就要说到三级缓存的话题,spring引入一个三级缓存来解决循环依赖和AOP的问题。三级缓存的key还是为beanName,但是value是一个函数(ObjectFactory#getBean方法),在该函数中执行获取早期对象的逻辑:getEarlyBeanReference方法。 在getEarlyBeanReference方法中,Spring会调用所有SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,通过该方法可以修改早期对象的属性或者替换早期对象。这个是Spring留给开发者的另一个扩展点。

源码分析

该扩展接口有3个触发回调方法

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {

	/**
	 * 预测最终从此处理器的回调中返回的 bean 的类型。
	 * @param beanClass  bean 的原始类
	 * @param beanName   bean 的名称
	 */
	@Nullable
	default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	/**
	 * 确定要用于给定 bean 的候选构造函数。默认实现返回null   该回调发生在 postProcessBeforeInstantiation之后
 	 * @param beanClass  bean 的原始类
	 * @param beanName   bean 的名称
	 */
	@Nullable
	default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
			throws BeansException {

		return null;
	}

	/**
	 * 获取对指定 bean 的早期访问的引用,通常用于解决循环引用  该回调发生在 postProcessAfterInstantiation之后
	 * 此回调使后处理器有机会尽早公开包装器 - 即在目标 bean 实例完全初始化之前。公开的对象应等同于否则将公开的内容
	 * @param beanClass  bean 的原始类
	 * @param beanName   bean 的名称
	 */
	default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

接下来我们来看看调用的一个链路getBean=》doGetBean=》createBean=》doCreateBean

org.springframework.beans.factory.support.AbstractBeanFactory#getBean


@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

	String beanName = transformedBeanName(name);
	Object beanInstance;

	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}

	// Create bean instance.
	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName, () -> {
			try {
				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process, to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		});
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
	//.....源码省略
}

createBean方法主要调用doCreateBean方法,在doCreateBean调用之前会先调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation拦截bean的实例化,如果这里的后置处理器返回了bean,则不会到后面的doCreateBean方法中
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {
		
	//.....源码省略
	try {
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		if (logger.isTraceEnabled()) {
			logger.trace("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}
	//.....源码省略
}			

对于循环依赖的情况,getBean(A)–》存入正在创建缓存–》存入三级缓存–》populateBean(A)–》getBean(B)–》populateBean(B)–》getBean(A)–》getSingleton(A),当在populateBean(B)的过程中调用getSingleton(A)的时候,明显一级缓存和二级缓存都为空,但是三级缓存不为空,所以会通过三级缓存获取bean,三级缓存的创建逻辑如下:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

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

	//.....省略

	// 缓存单例,以便能够解决循环引用
	// 即使是由 BeanFactoryAware 等生命周期接口触发。
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
	//.....省略

这个getEarlyBeanReference方法的逻辑很简单,该方法主要就是对SmartInstantiationAwareBeanPostProcessor后置处理器的调用,而循环依赖时的AOP就是通过这个SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法实现的,相关的具体类是AnnotationAwareAspectJAutoProxyCreator

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	//如果容器中有InstantiationAwareBeanPostProcessors后置处理器
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
			//调用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法
			exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
		}
	}
	return exposedObject;
}

在getSingleton方法的逻辑中,先从一级缓存获取,如果一级缓存没有找到,那么如果获取的bean正在创建中,则从二级缓存获取,如果二级缓存没有找到,那么从三级缓存获取,三级缓存中存的是ObjectFactory实现,最终会调用其getBean方法获取bean,然后存入二级缓存中,同时清除三级缓存。同时提供了一个allowEarlyReference参数控制是否能从三级缓存中获取
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton

/**
 * 返回以给定名称注册的(原始)单例对象。检查已实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)。
 * @param beanName 要查找的 bean 的名称
 * @param allowEarlyReference 是否应创建早期引用
 * @return 已注册的单例对象,如果未找到则返回 null
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							singletonObject = singletonFactory.getObject();
							this.earlySingletonObjects.put(beanName, singletonObject);
							this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}
}

示例

@Slf4j
@Component
public class ExtendSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        log.info("predictBeanType run {}" ,beanName);
        return beanClass;
    }

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        log.info("determineCandidateConstructors run {}" ,beanName);
        return null;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        log.info("getEarlyBeanReference run {}" ,beanName);
        return bean;
    }
}

在这里插入图片描述

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

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

相关文章

Python基础学习(1)

目录 一&#xff0c;表达式和变量 1&#xff0c;表达式 2&#xff0c;变量 二&#xff0c;类型 1&#xff0c;前言 2&#xff0c;动态性特征 一个技巧让python变量类型更加明确 3&#xff0c;变量的类型 1&#xff0c;整型 2&#xff0c;float 3&#xff0c;布尔型 4&…

border制作渐变色边框

border也可以做出渐变色效果 .themeConfig {width: 250px;height: 117px;border: 1.5px solid transparent !important;border-radius: 8px !important;background-clip: padding-box, border-box;background-origin: padding-box, border-box;background-image: linear-gradi…

linux第一课(操作系统核心)

一.关于linux (1)linux是一款开源的操作系统(是多用户&#xff0c;多任务&#xff0c;多线程)。 (2)一般所说的linux指的是linux核心&#xff0c;即对计算机硬件资源负责调度管理&#xff0c;主要职责是进程管理&#xff0c;内存管理文件系统&#xff0c;设备驱动&#xff0c…

轧钢测径仪对热轧产线实现温度系数自动修正!

钢材在轧制过程中&#xff0c;需要检测外径时&#xff0c;很多都处于高温状态&#xff0c;甚至是1000℃以上&#xff0c;这对轧钢测径仪是个巨大的考验&#xff0c;但测径仪在解决高温、水雾、氧化铁皮、震动等对测量影响的同时&#xff0c;还要考虑钢材热态下的膨胀造成的测量…

揭秘同态加密 (HE):你需要知道什么?

鉴于苹果公司发布了重要的 Swift 同态加密公告&#xff0c;Zama 高级软件工程师 Agns Leroy 详细解释了 HE 是什么、不同的方案及其功能&#xff0c;以及它在未来可能的用途。 7 月底&#xff0c;苹果隐私保护技术团队宣布了一项令人兴奋的新成果&#xff1a;‘swift-homomorp…

什么是WSL?

WSL&#xff08;Windows Subsystem for Linux&#xff09;是一个为在 Windows 10 和 Windows 11 上能够原生运行 Linux 二进制可执行文件的兼容层。 主要特点和功能包括&#xff1a; 一、运行 Linux 环境 允许用户在 Windows 系统上直接运行 Linux 命令行工具、脚本和应用程…

springboot系列--自动配置原理

一、容器功能 一、组件添加功能 一、Configuration Configuration有两种模式&#xff0c;Full模式与Lite模式。 1、配置 类组件之间无依赖关系用Lite模式加速容器启动过程&#xff0c;减少判断 2、配置类组件之间有依赖关系&#xff0c;方法会被调用得到之前单实例组件&#…

linux 操作系统下cupsdisable命令介绍和使用案例

linux 操作系统下cupsdisable命令介绍和使用案例 cupsdisable 命令是 Linux 操作系统中用于禁用 CUPS&#xff08;通用打印服务&#xff09;打印机的命令。它允许用户将指定的打印机设置为不可用状态&#xff0c;从而阻止任何新的打印作业被发送到该打印机 cupsdisable 命令概…

句子成分——每日一划(七)

目录 一、原句 二、第一部分 三、第二部分 一、原句 Such a state of affairs can only produce antagonism between the laboring class and the owning, i.e., do-nothing, class. The fight breaks out and hatred delivers its blows. 来源&#xff1a;Why I Was a Bur…

C++11第四弹:包装器

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

探索UWB技术的独特优势:实现高精度定位

UWB定位技术是一种利用无线信号进行精确位置定位的技术&#xff0c;它利用超宽带无线电信号通过测量信号的到达时间、相位差和信号能量等参数来确定物体的精确位置。 UWB定位技术具有多种优势&#xff0c;首先&#xff0c;它具有较高的定位精度&#xff0c;可实现毫米级的精确…

如何防止ZIP压缩文件被随意打开?

ZIP文件是常见的压缩文件格式&#xff0c;为了保护压缩包不被随意打开&#xff0c;很多人还会给ZIP压缩包设置密码&#xff0c;用于保护文件的数据安全&#xff0c;以下是一篇关于如何防止ZIP压缩文件被随意打开的详细探讨。 引言 ZIP文件因其高效的压缩率和广泛的兼容性&…

目标检测中的解耦和耦合、anchor-free和anchor-base

解耦和耦合 写在前面 在目标检测中&#xff0c;objectness&#xff08;或 objectness score&#xff09;指的是一个评分&#xff0c;用来表示某个预测框&#xff08;bounding box&#xff09;中是否包含一个目标物体。 具体来说&#xff0c;YOLO等目标检测算法需要在每个候选区…

基于OpenSSL的密码管理系统-应用密码学课程报告

第1章 概要设计 1.1 设计目的 本研究旨在设计并实现一个基于OpenSSL的密码管理系统&#xff0c;该系统具备密钥对的生成、密钥上传、密钥的核对、身份认证、文件与邮件的加密和解密、数字签名及数字证书管理等常用功能。研究的意义主要体现在以下几个方面&#xff1a; 提升网…

Ubuntu20-xrdp与Windows-mstsc远程桌面连接

ubuntu端 sudo adduser yu //输入密码和确认密码&#xff0c;后面一路回车&#xff0c;新建用户yu&#xff0c;确保用户没有被登录 sudo apt install xrdp //安装xrdp sudo systemctl status xrdp //查看xrdp服务状态 sudo adduser xrdp ssl-cert //将用户 xrdp 添加到 ss…

悟空crm客户管理系统二次开发 单独新增表格字段

1&#xff0c;仪表盘&#xff08;数据来源修改&#xff09; 注意点&#xff1a;有层级关系&#xff0c;管理员账号可以看到全部数据&#xff0c;主管只能看到下属数据。 2、在客户管理菜单里面 增加一个时间筛选、额度汇总 /*** 获取客户列表** param $type* param $content*…

在线API文档,技术文档工具源码ShowDoc

ShowDoc是一个非常适合IT团队的在线API文档、技术文档工具。通过showdoc&#xff0c;你可以方便地使用markdown语法来书写出美观的API文档、数据字典文档、技术文档、在线excel文档等等。 代码下载

单机docker-compose部署minio

单机多副本docker-compose部署minio 简单介绍 如果服务器有限可以单机挂载多硬盘实现多副本容错&#xff08;生产不推荐&#xff09; 部署好的文件状态 有两个重要文件 docker-compose.yaml和nginx.conf docker-compose.yaml是docker部署容器的配置信息包括4个minio和1个ng…

云微客全流程闭环,实现在短视频营销中快速拿结果

不知道大家有没有在抖音或者是其他短视频平台见过这样的视频&#xff0c;这一类的视频制作的非常简单&#xff0c;只有一个简单的文字搭配上背景素材&#xff0c;但是它的播放量和互动量却是惊人的好。在短视频领域的朋友想必有过这样心声&#xff0c;这么好的播放量&#xff0…

USB虚拟串口——CDC ACM 虚拟串口(不使用 IAD)

文章目录 CDC ACM 虚拟串口实现描述符结构设备描述符配置描述符集合配置描述符接口 1 的描述符接口描述符类特殊描述符输入端点描述符接口 2 的描述符接口描述符输出端点描述符输入端点描述符类特殊请求set control line statusget line codingset line codingCDC 数据交互主机…