Spring中的MergedBeanDefinitionPostProcessor有什么作用 ?

news2024/11/29 2:44:06

Spring中的MergedBeanDefinitionPostProcessor有什么作用 ?

  • 引言
  • 调用时机
  • 加载bean定义的几种方式
  • postProcessMergedBeanDefinition接口作用
  • 小结


引言

MergedBeanDefinitionPostProcessor这个Bean后置处理器大家可能关注的比较少,其本身也只提供了一个bean生命周期回调接口:

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
	void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
}

虽然这个bean生命周期回调接口可能并没有起到关键的作用,但是理解该接口的作用,还是会对我们理解整个Bean的初始化流程起着重要作用。

所以本文就来简单聊聊该接口的意义所在。


调用时机

我们先来看看该接口的调用时机:
在这里插入图片描述
postProcessMergedBeanDefinition回调接口是在MergeBeanDefintion和实例化之后进行的调用,目的是为了对合并后的BeanDefintion进行后置处理,那么后置处理具体包含什么逻辑呢?


加载bean定义的几种方式

SpringBoot中提供了四种加载Bean定义的方式,如下所示:
在这里插入图片描述
对于不同方式加载得到的Bean,会封装为不同类型的BeanDefintion :

  • XML 定义 Bean:GenericBeanDefinition
  • @Component 以及派生注解定义 Bean:ScannedGenericBeanDefinition
  • 借助于 @Import 导入 Bean:AnnotatedGenericBeanDefinition
  • @Bean 定义的方法:ConfigurationClassBeanDefinition 私有静态类(ConfigurationClassBeanDefinitionReader的内部类)

BeanDefintion更多信息可参考: SpringIOC之BeanDefinition 相关类型关系

对于不同方式导入的Bean定义,如果存在重复对同一个Bean的定义,则会根据allowBeanDefinitionOverriding属性是否设置为true,判断是否允许Bean定义的覆盖,如果不允许,则抛出异常。

而在Bean实例化之前,会进行BeanDefinition类型的归一化,即 mergeBeanFintion ,统一转换为RootBeanfintion进行后续处理。当然,这里的merge更多指代的是父子Bean定义的合并。


postProcessMergedBeanDefinition接口作用

我们可以通过上面几种方式声明Bean的定义,并且在具体的Bean类中通过@Autowired等注解进行运行时依赖注入,那么这里就会存在一个问题:

  • 我们通过xml配置文件声明bean定义的时候,同样可以通过xml配置来声明依赖注入点,那么如果此时xml配置声明的依赖注入点和注解方式声明的依赖注入点产生重叠了,那么此时谁的优先级更高呢?

为了处理这个问题,Spring提供了MergedBeanDefinitionPostProcessor这个Bean后置处理器,由其负责处理xml配置的依赖注入点和注解配置的依赖注入点重叠问题。

这里以处理@Autowired和@Value注解的AutowiredAnnotationBeanPostProcessor为例,看看它的postProcessMergedBeanDefinition方法都做了什么事情:

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

findAutowiringMetadata方法负责寻找当前bean的字段和方法上使用@Autowired和@Value注解声明的依赖注入点,并为每个依赖注入点封装一个InjectElement,然后为当前bean创建一个InjectionMetadata,负责管理当前bean上所有InjectElement:

	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs){
		// InjectionMetadata会被缓存起来--key为beanName
		String 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构建InjectionMetadata
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}
   
	private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;
		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			//遍历当前类上所有属性,寻找到存在相关注解的属性
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				AnnotationAttributes ann = findAutowiredAnnotation(field);
				//排除静态属性的注入
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					//从注解中取出required属性--表明是否必须注入成功
					boolean required = determineRequiredStatus(ann);
					//封装为AutowiredFieldElement后返回
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
            //遍历当前bean所有方法,寻找存在相关注解的方法,并且方法不是静态的,封装为AutowiredMethodElement后返回
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				...
				AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						...
						return;
					}
					...
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});
			elements.addAll(0, currElements);
			//注入依赖注入一并处理当前父类上标注的相关依赖注入点
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
        //创建一个InjectionMetadata返回,InjectionMetadata管理当前bean中所有依赖注入点
		return new InjectionMetadata(clazz, elements);
	}	    
}

metadata.checkConfigMembers(beanDefinition); 方法考虑可能存在多个注解(例如 @Autowired、@Resource)同时标注在同一个属性上,因此需要避免重复处理的问题。

在Spring中,多个注解可以同时标注在同一个属性上,用于指定不同的依赖注入方式或配置信息。但是,这可能导致在处理依赖注入时重复处理同一个属性,从而引发错误或不一致的行为。

为了避免重复处理,checkConfigMembers() 方法会检查配置类中的成员元素,并通过 RootBeanDefinition 的 registerExternallyManagedConfigMember() 方法将已处理的成员标记为外部管理的配置成员。这样,在Spring容器后续的处理过程中,如果遇到同一个成员被多次标注的情况,Spring容器会忽略重复的处理,并保持一致性。

举例来说,假设在配置类中有一个属性 myDependency,同时被 @Autowired 和 @Resource 注解标注:

@Autowired
@Resource
private MyDependency myDependency;

在调用 checkConfigMembers() 方法时,它会检查 myDependency 属性是否已经被标记为外部管理的配置成员。如果没有被标记,它会将其注册为外部管理的配置成员。这样,在Spring容器后续的处理过程中,如果遇到重复的依赖注入标记,例如另一个地方使用了 @Resource 注解标注了 myDependency,Spring容器会忽略重复的处理,保持一致性。

总结:checkConfigMembers() 方法的作用之一是考虑可能存在多个注解同时标注在同一个属性上的情况,避免重复处理。通过将已处理的成员标记为外部管理的配置成员,它确保Spring容器在处理依赖注入时不会重复处理同一个属性。

该方法具体源码如下:

	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;
	}

在这里插入图片描述


小结

MergedBeanDefinitionPostProcessor后置处理器在Spring的实际应用中起到了两个作用:

  • 初始化当前bean的InjectionMetadata缓存
  • 过滤掉已经处理过的依赖注入点

当然,这只是Spring中给出的应用,我们也可以在该接口中玩出更多的花样。

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

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

相关文章

iptable 防火墙一

目录 iptables概述netfilter/iptables 关系四表五链四表&#xff1a;五链&#xff1a; 规则链之间的匹配顺序主机型防火墙&#xff1a;规则链内的匹配顺序&#xff1a; iptables 安装iptables防火墙的配置方法&#xff1a;iptables 命令行配置方法&#xff1a;常用的控制类型&a…

一文读懂大语言模型

以ChatGPT为代表的大语言模型被很多人认为是新一轮科技革命的起点&#xff0c;本文旨在通过概念性介绍&#xff0c;让普通人能够尽可能理解人工智能以及大语言模型的基本概念&#xff0c;从而了解这些技术能做以及不能做什么。原文: A Very Gentle Introduction to Large Langu…

v4l2数据结构分析

v4l2数据结构分析 文章目录 v4l2数据结构分析Video4Linux2设备v4l2_device媒体设备media_deviceVideo4Linux2子设备v4l2_subdevVideo4Linux2子设备的操作集v4l2_subdev_opsVideo4Linux2子设备的内部操作集v4l2_subdev_internal_opsVideo4Linux2控制处理器v4l2_ctrl_handlerVide…

微信自动聊天机器狗,配置chatGPT,比Siri还智能!

大家好&#xff0c;我是TheWeiJun&#xff1b;最近看见微信里各个群聊都在聊chatGPT&#xff0c;甚至有的大佬们都把chatGPT接入了微信群聊&#xff0c;于是就有粉丝来找小编&#xff0c;希望能出一期chatGPT的文章&#xff1b;故今天这篇文章我将手把手教大家如何实现并自定义…

学习《信息系统项目管理师教程》第4版应关注的PMBOK的巨大变化

学习《信息系统项目管理师教程》第4版应关注的PMBOK的巨大变化 《信息系统项目管理师教程》的第4版比起第3版来有不少变化。但是&#xff0c;这种变化完全没有体现出PMBOK第7版带来的巨大变化。 因为&#xff0c;在从《信息系统项目管理师教程》第3版出版的2017年到现在&…

uvc驱动中的v4l2

uvc驱动中的v4l2 文章目录 uvc驱动中的v4l2v4l2_device_registervideo_register_devicev4l2_ioctlsvideo_usercopy v4l2_device_register /driver/media/v4l2-core/v4l2-device.c uvc_probe->v4l2_device_register v4l2_device_register 只是用于初始化一些东西&#xff0c…

【数项级数】无穷个数相加一定是个数吗?

数项级数 引入思考问题转化 定义总结重要的例子练习题 引入 思考 数项级数&#xff0c;其实就是要解决无穷个数相加的问题。 而对于无穷求和的问题&#xff0c;思考&#xff1a;无穷个数相加一定是个数吗&#xff1f; 下面&#xff0c;我们来举几个例子&#xff1a; 1 2 2 …

创世纪:比特币诞生记

比特币的诞生 1. 创始区块2. 第一个举手的人3. 比特币的疯狂 1. 创始区块 2008年10月31日纽约时间下午2点10分&#xff0c;自称中本聪的人向一个邮件列表&#xff0c;包括密码学专家和爱好者几百个成员&#xff0c;发送了一封电子邮件。“我一直在研究一个新的电子现金系统&am…

springboot旅游资源管理系统门票酒店预订系统_b0a6b

Spring Boot 是 Spring 家族中的一个全新的框架&#xff0c;它用来简化Spring应用程序的创建和开发过程。也可以说 Spring Boot 能简化我们之前采用SSM&#xff08;Spring MVC Spring MyBatis &#xff09;框架进行开发的过程。config&#xff1a;主要用来存储配置文件&#…

chatgpt赋能Python-pythoncontinue怎么用

Python continue语句&#xff1a;提高代码效率的绝佳工具 什么是Python continue语句&#xff1f; Python的continue语句可以使循环跳过当前的迭代。这意味着如果在循环内部存在满足某特定条件的语句&#xff0c;那么我们就可以使用continue语句跳过当前循环。Python中的cont…

【Nodejs】使用Nodejs搭建HTTP服务,并实现公网远程访问

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 转载自内网穿透工具的文章&#xff1a;使用Nodejs搭建HTTP服务&#xff0c;并实现公网远程访问「内网穿透」 前言 Node.js…

Mybatis Plus实现乐观锁

文章目录 1 概念2 实现思路3 实现步骤步骤1:数据库表添加列步骤2:在模型类中添加对应的属性步骤3:添加乐观锁的拦截器步骤4:执行更新操作 1 概念 在讲解乐观锁之前&#xff0c;我们还是先来分析下问题: 业务并发现象带来的问题 : 秒杀 假如有100个商品或者票在出售&#xff…

【数据分享】我国各县1992—2019年社会经济指标(7个指标\无需转发)

社会经济指标常用于各项研究中&#xff0c;之前基于《中国城市统计年鉴》我们整理了1999-2020年地级市的地区生产总值及一二三产构成数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 我们发现在学者Chen Yahan在Figshare 数据库中提供了1992—2019年以县为…

动态规划-状态压缩DP

[SCOI2005] 互不侵犯 题目描述 https://www.luogu.com.cn/problem/P1896 在NN的棋盘里面放K个国王&#xff0c;使他们互不攻击&#xff0c;共有多少种摆放方案。国王能攻击到它上下左右&#xff0c;以及左上左下右上右下八个方向上附近的各一个格子&#xff0c;共8个格子。 …

ADS-B接收机Radarcape

1.设备简介 Radarcape是一款便携、高性能、功能强大的ADS-B地面接收机。Radarcape的设备清单包含&#xff1a;ADS-B接收机主机&#xff0c;专业级ADS-B天线&#xff0c;GPS天线&#xff0c;电源线&#xff0c;网线。 2. 功能特点 Radarcape可以通过网口输出飞机的原始数据D…

开源字节 CRM 系统

开源字节CRM是一款SaaS模式的客户关系管理软件&#xff0c;基于钉钉平台进行研发&#xff0c;以客户管理为核心&#xff0c;包含客户管理、销售全流程管理&#xff0c;合同订单、工单管理、移动审批、数据分析六大模块。 旨在助力企业销售全流程精细化、数字化管理&#xff0c…

Godot引擎 4.0 文档 - 入门介绍 - Godot简介

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a;Introduction to Godsot — Godot Engine (stable) documentation in English Godot简介 本文旨在帮助您确定 Godot 是否适合您。我们将介绍该引擎的一些广泛功能&#…

Linux中文件描述符fd和文件指针filp的理解

简单归纳&#xff1a;fd只是一个整数&#xff0c;在open时产生。起到一个索引的作用&#xff0c;进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。 文件描述符的操作(如: open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件…

Linux Audio (4) DAPM-1 Kcontrol

DAPM-1 Kcontrol 控制部件之kcontrolsnd_kcontrol_new 结构体如何定义snd_kcontrol_new?如何使用snd_kcontrol&#xff1f;添加kcontrol代码分析 课程&#xff1a;韦东山音频专题 内核&#xff1a;Kernel 3.5 但是我用的实例和课程不同&#xff0c;以防止编程记流水账 控制部件…

【周末闲谈】你知道物联网技术吗?

连接万物&#xff0c;创造未来。从智能家居到智慧医疗&#xff0c;从智能车联到智慧城市&#xff0c;物联网技术的影响已经悄然渗透到了我们的方方面面。欢迎大家积极讨论联网技术如何影响了我们的生活。 个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【…