Spring扩展点系列-SmartInstantiationAwareBeanPostProcessor

文章目录

    • 简介
    • 源码分析
    • 示例

简介

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 的名称*/@Nullabledefault Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {return null;}/*** 确定要用于给定 bean 的候选构造函数。默认实现返回null   该回调发生在 postProcessBeforeInstantiation之后* @param beanClass  bean 的原始类* @param beanName   bean 的名称*/@Nullabledefault 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 lockObject 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 locksingletonObject = 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 {@Overridepublic Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {log.info("predictBeanType run {}" ,beanName);return beanClass;}@Overridepublic Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {log.info("determineCandidateConstructors run {}" ,beanName);return null;}@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {log.info("getEarlyBeanReference run {}" ,beanName);return bean;}
}

在这里插入图片描述

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

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

相关文章

Nginx.conf没有server和location模块的解决方法

网上有些说法说自己在配置文件里面添加server和location模块&#xff0c;但是我发现好像可以不用&#xff0c;其实nginx的配置文件还是给了我们提示的&#xff0c;如图&#xff1a; 在最后一行其实引入了另一个配置文件&#xff0c;我们cd进去看一下有什么内容。输入ls命令发现…

网络药理学:15、草稿暂存区(autodock vina)

TCMSP 韦恩图在线网站 https://bioinfogp.cnb.csic.es/tools/venny/index.html String数据库参数详解&#xff1a;https://www.bilibili.com/video/BV1q64y1k7Zf?p16&vd_sourceaed4c634975918b14b7354ec93ce5389 David数据库可以用基因ID或者基因名。 KEGG数据库使用&am…

MYSQL数据库进阶篇——存储函数

存储函数是有返回值的存储过程&#xff0c;所有参数只能是IN类型 语法如下&#xff1a; 例如&#xff1a;

[项目] - Calc计算器

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天来尝试模拟windows 下的clac计算器 绘制计算器 拖动工具箱的Edit Control输入框、Button按钮 制作计算器界面需要将Edit Control输入框 拉长&#xff0c;将多行、只读 设置为True整体计算机的控件ID&#xff1a;I…

Ansys HFSS的边界条件与激励端口

本文将介绍HFSS边界条件、激励端口,然后重点介绍连接器信号完整性仿真应用最多的波端口(wave port)及其尺寸设置要点。 HFSS (电磁仿真)边界条件 HFSS中所谓的边界并非真正意义上的边界,边界条件是指定问题区域和对象边缘的场行为接口。在HFSS的背景下,边界的存在主要有两个…

缺陷(Bug)的一生

Bug 像是一个被过分宠爱的小孩子&#xff0c;得到了特别多的关注。它们在开发者的 IDE 里悄然无声的诞生&#xff0c;但在现身之刻却引来一片喧闹。 对于测试工程师发现的 Bug&#xff0c;它们的生命是这样的&#xff1a;测试工程师发现Bug&#xff0c;花些时间细细品味。 这…

GB28181应急救援行业视音频解决方案探究和技术实现

技术背景 应急救援是一项针对突发、具有破坏力的紧急事件采取预防、预备、响应和恢复的活动与计划。这些紧急事件可能包括自然灾害&#xff08;如地震、洪水、台风&#xff09;、事故灾难&#xff08;如火灾、爆炸、交通事故&#xff09;、公共卫生事件&#xff08;如疫情、食…

pycharm安装-教程

在研究和学习的过程中&#xff0c;Jupyter Notebook确实是一个不错的选择&#xff0c;但在处理大型项目程序时&#xff0c;可能需要一个更强大的集成开发环境(IDE)。在此背景下&#xff0c;我们推荐使用PyCharm。PyCharm不仅具备多种功能&#xff0c;如项目管理、环境管理、代码…

叉车智能ai防撞系统解决方案

现代物流运输和制造业领域&#xff0c;叉车作为重要作业工具&#xff0c;使用范围遍及工厂车间、仓库、码头、物流中心等场所&#xff0c;发挥着不可或缺的主力作用。然而&#xff0c;由于叉车车身庞大、视野盲区多&#xff0c;如操作不当极易导致撞击事故&#xff0c;对作业人…

第一届长城杯信息安全铁人三项赛决赛 取证溯源 (复现)

前言&#xff1a; 2024铁人三项决赛应急响应 您的同事李白在运维一台部署了移动应用服务端的linux服务器时发现了异常&#xff0c;好像被黑客攻 击了。小李通过简单分析&#xff0c;发现可能是由于公司的移动应用和其服务端程序都存在安全问题导致 的。小李将当天可能与攻击相关…

在国行iOS设备上启用Apple Intelligence

昨天凌晨&#xff0c;科技春晚隆重举行&#xff0c;厨子大叔如期发布最新iPhone 16系列&#xff0c;开启iPhone的AI时代。可惜&#xff0c;中国大陆用户此刻并不能享受上述服务。但不用担心&#xff0c;下面猫叔就教你如何在国行iOS设备上启用Apple Intelligence。 预备工作 1…

c++基础入门一

文章目录 C基础入门&#xff08;一&#xff09;一、C简介二、C入门一、命名空间食用方法 二、输入/输出三、缺省参数 C基础入门&#xff08;一&#xff09; 一、C简介 ​ C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大…

python版本dikstra堆优化

题目&#xff1a; 代码&#xff1a; from heapq import * #堆优化版本的最短路算法&#xff08;dijkstra&#xff09; N150010 h[-1 for _ in range(N)] e[-1 for _ in range(N)] ne[-1 for _ in range(N)] w[-1 for _ in range(N)] idx0 st[False for _ in range(N)] dist[fl…

Shopee虾皮:广告类型选择与效果优化要点

Shopee虾皮作为东南亚增势迅猛的电商平台&#xff0c;是很多跨境卖家出海东南亚的首要选择。这势必带来强烈的竞争&#xff0c;因此&#xff0c;如果卖家想要突出重围&#xff0c;广告投放和优化则格外重要。 一、虾皮的广告类型 1.关键词广告 当买家搜索的关键字与卖家投放的…

Qt 弹出菜单右键菜单 QMenu 设置不同颜色的子项

概述 在Qt中&#xff0c;可以使用样式表&#xff08;StyleSheet&#xff09;来自定义 QMenu 的外观&#xff0c;包括其子项&#xff08;如菜单项QAction&#xff09;的颜色。但是&#xff0c;这通常可以设置 QMenu 的整体样式&#xff0c;而不能单独设置某个子项的颜色。不过&…

什么是 SMB 服务器以及它如何工作?

在本文中&#xff0c;您将了解 SMB 服务器以及它们如何促进网络文件共享。 我们将介绍它们的基本功能、主要特性以及如何安全地设置它们。无论您是新手还是需要复习&#xff0c;本指南都将帮助您更好地了解 SMB 服务器。 什么是 SMB 服务器&#xff1f; SMB&#xff08;服务器…

C语言浮点型数据在内存中的存储(23)

文章目录 前言一、浮点数在内存中的存储练习引入浮点数的存储浮点数存的过程 二、浮点数取的过程E不全为0或不全为1E全为0E全为1 三、再回顾练习总结 前言 哎&#xff0c;之前写了一篇&#xff0c;可是中途退出没保存&#xff0c;只能再写一遍了~   浮点数在内存中的存储跟整…

计算左边(比自己小的元素)的最长距离

前言&#xff1a;一般做的题目都是使用单调栈来求出距离这个点最近的那个比这个数大或小的元素&#xff0c;但是如果是需要找到最远的那个元素呢&#xff1f;我们可以用到类似逆序对的思路&#xff0c;我们先进行排序从小到大&#xff0c;接着我们先处理左边&#xff0c;每次维…

vue3使用vscode开发遇到热更新问题(文件保存页面不实时更新)

1.第一种情况是所有页面都不热更新 检查Live Server插件&#xff0c;确保安装&#xff0c;安装也无效可以试一下重新安装 2.只有个别页面没有热更新(本人是这种情况) 遇到这种情况就要检查路由文件中导入的文件名称与项目中文件名字是否一致(大小写也要一样) 若路由文件中 导…

利用shuji还原webpack打包源码

0 前言 前段时间做一个银行的项目&#xff0c;是在别人已经打过好多次的基础上继续打&#xff0c;而且时间很短&#xff0c;也是没办法要有产出&#xff0c;这个银行很多站点都是webpack打包&#xff0c;就新学了一个点&#xff1a;利用shuji获取webpack打包站源码&#xff08…