当前位置: 首页 > news >正文

Spring扩展点系列-MergedBeanDefinitionPostProcessor

文章目录

    • 简介
    • 源码分析
    • 示例
      • 示例一:Spring中Autowire注解的依赖注入

简介

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

MergedBeanDefinitionPostProcessor扩展点的作用是对合并后的BeanDefintion进行后置处理。准确的调用时机应该是在创建完 bean 实例之后,接下来会对该接口源码和举例进行说明该扩展点的使用

源码分析

该接口的顶级接口是BeanPostProcessor

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {/*** Post-process the given merged bean definition for the specified bean.* @param beanDefinition 处理后bean definition* @param beanType       解析出来的 bean 的 class* @param beanName       bean 的名字* @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors*/void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);/*** A notification that the bean definition for the specified name has been reset,* and that this post-processor should clear any metadata for the affected bean.* <p>The default implementation is empty.* @param beanName bean 的名字* @since 5.1* @see DefaultListableBeanFactory#resetBeanDefinition*/default void resetBeanDefinition(String beanName) {}}

找到 applyMergedBeanDefinitionPostProcessors可以看出, MergedBeanDefinitionPostProcessor 的调用是在创建完 bean 实例之后调用。applyMergedBeanDefinitionPostProcessors 方法会获取所有的 MergedBeanDefinitionPostProcessor,并回调它们的 postProcessMergedBeanDefinition 方法
回调 postProcessMergedBeanDefinition 方法时,已经拿到了 merged bean definition,并且还未开始 poplateBean 填充 bean 属性、initlializeBean 初始化 bean 对象,因此可以在这里对 merged bean definition 进行一些操作,在 poplateBean 或 initlializeBean 阶段使用前面操作结果实现所需功能

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}//.....源码省略
}protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);}
}

示例

示例一:Spring中Autowire注解的依赖注入

在Spring 中,AutowiredAnnotationBeanPostProcessor是MergedBeanDefinitionPostProcessor 的一个实现类,该类通过 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition 方法回调获取 bean 上的 Autowire 注解,并将相关信息保存到缓存中。整体的主要作用是完成 Autowire 注解的依赖注入
在这里插入图片描述

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);//checkConfigMembers方法就是遍历从findAutowiringMetadata方法拿到的数据,返回封装这些数据,最后放入到RootBeanDefinition中,也就是bean定义信息中metadata.checkConfigMembers(beanDefinition);
}private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// InjectionMetadata会被缓存起来--key为beanNameString cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());//双重锁机制,确保单例InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}//为当前bean构建InjectionMetadatametadata = buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;
}private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {// 构建currElements,用来存储符合条件的数据final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();//遍历当前类上所有属性,寻找到存在相关注解的属性ReflectionUtils.doWithLocalFields(targetClass, field -> {//findAutowiredAnnotation方法就是在遍历autowiredAnnotationTypes,判断参数或方法是否被autowiredAnnotationTypes里面的注解修饰,有就直接返回了。MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {if (Modifier.isStatic(field.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static fields: " + field);}return;}boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}});//遍历当前类上所有方法,寻找到存在相关注解的属性且非静态方法,然后封装为AutowiredMethodElement后返回ReflectionUtils.doWithLocalMethods(targetClass, method -> {// 查找符合条件的方法Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (Modifier.isStatic(method.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static methods: " + method);}return;}if (method.getParameterCount() == 0) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation should only be used on methods with parameters: " +method);}}// 获取required参数boolean required = determineRequiredStatus(ann);PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);// 添加到currElementscurrElements.add(new AutowiredMethodElement(method, required, pd));}});elements.addAll(0, currElements);//注入依赖注入一并处理当前父类上标注的相关依赖注入点targetClass = targetClass.getSuperclass();}// 向上遍历,直到父类为Object为止。while (targetClass != null && targetClass != Object.class);//创建一个InjectionMetadata返回,InjectionMetadata管理当前bean中所有依赖注入点return InjectionMetadata.forElements(elements, clazz);
}public void checkConfigMembers(RootBeanDefinition beanDefinition) {Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());for (InjectedElement element : this.injectedElements) {Member member = element.getMember();if (!beanDefinition.isExternallyManagedConfigMember(member)) {beanDefinition.registerExternallyManagedConfigMember(member);checkedElements.add(element);}}this.checkedElements = checkedElements;
}public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {//后续依赖注入只会对checkedElements集合中的依赖注入点进行处理Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}
}

相关文章:

Spring扩展点系列-MergedBeanDefinitionPostProcessor

文章目录 简介源码分析示例示例一&#xff1a;Spring中Autowire注解的依赖注入 简介 spring容器中Bean的生命周期内所有可扩展的点的调用顺序 扩展接口 实现接口ApplicationContextlnitializer initialize AbstractApplicationContext refreshe BeanDefinitionRegistryPos…...

Centos 7.9 使用 crontab 实现开机启动

[rootlocalhost ~]# crontab -e [rootlocalhost ~]# reboot # crontab -e reboot /path/to/my/program # reboot 表示重启开机的时候运行一次 reboot /test/hello.sh 参考&#xff1a; Linux crontab 命令 https://www.runoob.com/linux/linux-comm-crontab.html Run prog…...

基于微信的设备故障报修管理系统设计与实现+ssm论文源码调试讲解

2相关技术 2.1微信小程序 小程序是一种新的开放能力&#xff0c;开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播&#xff0c;同时具有出色的使用体验。尤其拥抱微信生态圈&#xff0c;让微信小程序更加的如虎添翼&#xff0c;发展迅猛。 2.2 MYSQL数据…...

yolo自动化项目实例解析(二)ui页面整理 1.78

我们在上一章整理main.py 的if __name__ __main__: 内容还留下面这一段&#xff0c; from PyQt5.QtWidgets import *from lanrenauto.moni.moni import *from PyQt5.QtGui import *app QApplication(sys.argv) # 初始化Qt应用ratio screen_width / 2560 # 分辨率比例# 设…...

PyQt / PySide + Pywin32 + ctypes 自定义标题栏窗口 + 完全还原 Windows 原生窗口边框特效项目

项目地址&#xff1a; GitHub - github201014/PyQt-NativeWindow: A class of window include nativeEvent, use PySide or PyQt and Pywin32 and ctypesA class of window include nativeEvent, use PySide or PyQt and Pywin32 and ctypes - github201014/PyQt-NativeWindow…...

面试时遇见的项目问题

汽车在线销售平台项目 项目的甲方是谁&#xff1f; 甲方是一家汽车销售公司&#xff0c;他们希望通过互联网技术提升销售效率和服务质量 为什么要做这个项目&#xff1f; 很多消费者越来越倾向于在线上完成购车之前的大部分决策。所以甲方找到我们希望通过建立一个在线的销…...

在线骑行网站设计与实现

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装在线骑行网站软件来发挥其高效地信息处理的作用&#xff0c…...

大批量查询方案简记(Mybatis流式查询)

Mybatis的流式查询 摘要: 介绍使用mybatis流式查询解决大数据量查询问题. 1 业务背景 开发中遇到一个业务,说起来也很无奈:公司用的数据库MySQL,一张表里只保留了一个月的数据,但是数据量竟然高达2000W还要多,然后用户有个需求也很恶心,为了完成这个业务我需要定时任务每一个月…...

python - 子类为什么调用父类的方法

菜鸟教程 - 面向对象https://www.runoob.com/python3/python3-class.html为什么写这个呢 &#xff0c;因为很多时候&#xff0c;事情很简单&#xff0c;但我往往记住了使用方式&#xff0c;忘记了使用原因&#xff0c;也因为自己看到super()时&#xff0c;也想问为什么要用supe…...

【JavaScript】数据结构之字典 哈希表

字典 键值对存储的&#xff0c;类似于js的对象&#xff0c;但在js对象中键[key]都是字符串类型或者会转换成字符串类型&#xff0c;因此后声明的键值会覆盖之前声明的值。字典以map表示&#xff0c;map的键不会转换类型。 let map new Map() map.set(a, 1) map.set(b, 2) ma…...

Adobe出现This unlicensed Photoshop app has been disabled

Adobe Acrobat或Photoshop软件突然出现This unlicensed Photoshop app has been disabled 症状 解决方法 删除软件安装目录下的AcroCEF和acrocef_1l两个子文件夹。主要是为了删除AcroCEF.exe。 如果存在复发&#xff0c;则删除xxxxxxx\AdobeGCClient\AdobeGCClient.exe。 不…...

elementui 单元格添加样式的两种方法

方法一 <el-table-column fixed prop"name" label"姓名" width"120"> <<template scope"scope"> <span :class"{red:scope.row.color1,yell:scope.row.color2,green:scope.row.col…...

如何有效管理技术债务:IT项目中的长期隐患

如何有效管理技术债务&#xff1a;IT项目中的长期隐患 在软件开发和IT项目管理中&#xff0c;技术债务&#xff08;Technical Debt&#xff09;是一个经常被忽视却又至关重要的概念。技术债务就像金融债务一样&#xff0c;当我们在项目开发中选择了某些“捷径”来快速交付&…...

2024 “华为杯” 中国研究生数学建模竞赛(D题)深度剖析|大数据驱动的地理综合问题|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题&#xff01; CS团队倾注了大量时间和心血&#xff0c;深入挖掘解…...

Linux 清空redis缓存及查询key值

1.登录redis redis-cli -h 127.0.0.1 -p 6379# 如果有密码需要下面这一步 auth 你的密码直接带密码登录 redis-cli -h 127.0.0.1 -p 6379 -a 密码出现ok表示登录成功 2.标题查看所有key keys *3.查看某个key 的值 get keyName4.清空整个Redis服务器的数据 flushall5.查看…...

MySql调优(三)Query SQL优化(2)explain优化

explain执行计划出现以下情况&#xff0c;均需要优化&#xff1a; 一、Using temporary 查询执行过程中出现Using temporary提示&#xff0c;通常意味着MySQL需要创建一个临时表来存储中间结果。这种情况多发生在数据库优化器无法通过现有的索引直接有效地执行查询时&#xf…...

Java【代码 18】处理Word文档里的Excel表格数据(源码分享)

处理Word文档里的Excel表格数据 1.原始数据2.处理程序2.1 识别替换表格表头2.2 处理多余的换行符2.3 处理后的结果 3.总结 1.原始数据 Word 文档里的 Excel 表格数据&#xff0c;以下仅为示例数据&#xff1a; 读取后的字符串数据为&#xff1a; "姓名\r\n身份证号\r\n手…...

21、Tomato

难度 低(个人认为中) 目标 root权限 一个flag 使用VMware启动 kali 192.168.152.56 靶机 192.168.152.66 信息收集 端口信息收集 可以看到有个ftp服务&#xff0c;2211实际是ssh协议端口&#xff0c;80、8888是一个web服务 web测试 80端口显示一个tomato 查看源码给了一些…...

代码随想录 八股文训练营40天总结

参加训练营的话也是给自己一定的约束力&#xff0c;让自己能够定期去对八股文进行一个背诵&#xff0c;虽然说中间有几天放假&#xff0c;没有进行打卡外&#xff0c;还是比较完整的坚持下来了。 从计算机网络--操作系统--MySQL--Redis--C基础&#xff0c;虽然这些知识都有看过…...

Debian 12上安装google chrome

当前系统&#xff1a;Debian 12.7 昨天在Debian 12.7上安装Google Chrome时&#xff0c;可能由于网络原因&#xff0c;导入公钥始终失败。 导致无法正常使用命令#apt install google-chrome-stable来安装google chrome; 解决办法&#xff1a; Step1.下载当前google chrome稳…...

前端与后端

实例一 处理登录页面请求 # 处理登录页面请求 app.route(/c, methods[GET, POST]) # /c是网页地址 def login(): usernameaa passwordbb print(username,password) if request.method POST: username request.form.get(yhm) password requ…...

利用 Scrapy 构建高效网页爬虫:框架解析与实战流程

目录 前言1 Scrapy 框架概述1.1 Scrapy 的核心优势1.2 Scrapy 的典型应用场景 2 Scrapy 工作原理解析2.1 框架结构图2.2 Spider&#xff1a;定义数据采集策略2.3 Scheduler&#xff1a;调度请求与去重2.4 Downloader&#xff1a;网页下载器2.5 Item&#xff1a;结构化数据容器2…...

Java八股文智能体——Agent提示词(Prompt)

这个智能体能够为正在学习Java八股文的同学提供切实帮助&#xff1a;不仅可以帮你优化答案表述&#xff0c;还能直接解答八股文相关问题——它会以面试者的视角&#xff0c;给出贴合求职场景的专业回答。 将以下内容发送给任何一个LLM&#xff0c;他会按照你提示词的内容&…...

ONLYOFFICE文档API:更强的安全功能

在数字化办公时代&#xff0c;文档的安全性与隐私保护已成为企业和个人用户的核心关切。如何确保信息在存储、传输及协作过程中的安全&#xff0c;是开发者与IT管理者亟需解决的问题。ONLYOFFICE作为一款功能强大的开源办公套件&#xff0c;不仅提供了高效的文档编辑与协作体验…...

DeepSeekMath:突破开放式语言模型中数学推理能力的极限

摘要 由于数学推理具有复杂且结构化的特性,这对语言模型构成了重大挑战。在本文中,我们介绍了 DeepSeekMath 7B 模型,该模型在 DeepSeek-Coder-Base-v1.5 7B 模型的基础上,使用从 Common Crawl 获取的 1200 亿个与数学相关的标记,以及自然语言和代码数据继续进行预训练。…...

初识PS(Photoshop)

初识PS&#xff08;Photoshop&#xff09; 1、Photoshop界面 2、常用快捷键...

历年山东大学计算机保研上机真题

历年山东大学计算机保研上机真题 2025山东大学计算机保研上机真题 2024山东大学计算机保研上机真题 2023山东大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school 从1到100找质数 题目描述 从 1 1 1 到 100 100 100 中找出所有的质数。 输入格式 …...

基于mediapipe深度学习的虚拟画板系统python源码

目录 1.前言 2.算法运行效果图预览 3.算法运行软件版本 4.部分核心程序 5.算法仿真参数 6.算法理论概述 7.参考文献 8.算法完整程序工程 1.前言 虚拟画板系统基于计算机视觉与深度学习技术&#xff0c;通过摄像头捕获用户手部动作&#xff0c;利用 MediaPipe框架实现手…...

LLM 使用 MCP 协议及其原理详解

LLM 使用 MCP 协议及其原理详解 &#x1f9e0; 一、MCP 协议概述 1. MCP 是什么&#xff1f; MCP&#xff08;Modular Communication Protocol&#xff09;是一种面向语言模型设计的通用通信协议&#xff0c;其设计目标是&#xff1a; 模块化&#xff08;Modular&#xff0…...

类和对象:实现日期类

目录 概述 一.实现日期类的基本框架 二.实现比较的运算符重载 1.>的运算符重载 2.的运算符重载 3.其余的比较运算符重载 三.加减天数的运算符重载 1.,的运算符重载 2.-&#xff0c;-的运算符重载 3.对1和2的小优化 四.两个日期类相减的重载 1.&#xff0c;--的重…...