Spring扩展点系列-MergedBeanDefinitionPostProcessor

news2024/9/21 23:28:32

文章目录

    • 简介
    • 源码分析
    • 示例
      • 示例一: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为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(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);
				// 添加到currElements
				currElements.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);
		}
	}
}

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

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

相关文章

记录一个英语听力网站的开发

背景 在当前全球经济衰退的背景下&#xff0c;国内IT相关工作的竞争日益激烈。为了获得更多的职业机会&#xff0c;学习英语或许能为程序员打开一扇新的窗户。尤其是在国际化背景的远程工作中&#xff0c;英语协作沟通是必不可少的。 尽管我们大多数人从小到大都在学习英语&a…

使用Renesas R7FA8D1BH (Cortex®-M85)和微信小程序App数据传输

目录 概述 1 系统架构 1.1 系统结构 1.2 系统硬件框架结构 1.3 蓝牙模块介绍 2 微信小程序实现 2.1 UI介绍 2.2 代码实现 3 上位机功能实现 3.1 通信协议 3.2 系统测试 4 下位机功能实现 4.1 功能介绍 4.2 代码实现 4.3 源代码文件 5 测试 5.1 编译和下载代码…

RNN的反向传播

目录 1.RNN网络&#xff1a;通过时间反向传播(through time back propagate TTBP) 2.RNN梯度分析 2.1隐藏状态和输出 2.2正向传播&#xff1a; 2.3反向传播&#xff1a; 2.4问题瓶颈&#xff1a; 3.截断时间步分类&#xff1a; 4.截断策略比较 5.反向传播的细节 ​编辑…

大数据新视界 --大数据大厂之JavaScript在大数据前端展示中的精彩应用

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

浙大数据结构:05-树8 File Transfer

数据结构MOOC PTA习题 这道题考察并查集的操作&#xff0c;合并以及找根结点 机翻&#xff1a; 1、条件准备 node是数组存放1-N结点的根节点的&#xff0c;n为总结点数 #include <iostream> using namespace std;const int N 1e4 5; int node[N]; int n; 先初始化…

众数信科AI智能体政务服务解决方案——寻知智能笔录系统

政务服务解决方案 寻知智能笔录方案 融合民警口供录入与笔录生成需求 2分钟内生成笔录并提醒错漏 助办案人员二次询问 提升笔录质量和效率 寻知智能笔录系统 众数信科AI智能体 产品亮点 分析、理解行业知识和校验规则 AI实时提醒用户文书需注意部分 全文校验格式、内…

【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程

目录 1 -> 进程组 1.1 -> 什么是进程组 1.2 -> 组长进程 2 -> 会话 2.1 -> 什么是会话 2.2 -> 如何创建会话 2.3 -> 会话ID(SID) 3 -> 控制终端 4 -> 作业控制 4.1 -> 什么是作业(job)和作业控制(Job Control) 4.2 -> 作业号 4.3…

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令

console:/dev # cat ttyUSB1 & console:/dev # echo AT > ttyUSB1 20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令 2024/9/21 21:03 【必须】Android12/Linux&#xff08;Buildroot&#xff09;都必须要&#xff01; 4、【Android12默认打开U…

电脑硬件-机械硬盘

简介 机械硬盘是电脑的主要存储媒介之一&#xff0c;通常用于存储一些文件资料或者学习视频笔记等比较大的内容。 结构 采用磁盘存储数据&#xff0c;使用温彻斯特的结构&#xff0c;特有四个特点&#xff1a; 1.磁头、盘片和运动机构安装在一个密封的腔体内。 2.盘片告诉旋…

一图快速看懂flink source的设计实现

文章目录 整体来说多个处理流程是解偶的&#xff0c;这样可以在面对多数据源情况下&#xff0c;能更加的灵活。 下面只展示了&#xff0c;主要的一些流程 下面补充一点&#xff0c;读取文件状态的保存&#xff0c;切分信息用了一个 ListState 来保存。具体要保存的信息&#x…

day2-1 app端文章查看

首先一共三张表 然后大致过程就是三层架构 用mp实现 具体出现的问题 1 测试的时候后端代码启动不了 先在maven clean一下 具体流程 然后执行完之后建议把这三个模块的target文件删除一下再运行 最后的话 如果还是报错 也是正常的 因为后边的东西都没写有些文件没有用到 2…

常见的中间件漏洞

Tomcat CVE-2017-12615 访问主页进行抓包 修改传参方式为put 放包进行连接 后台弱⼝令部署war包 访问主页试用默认账号密码tomcat/tomcat进行登录后 将哥斯拉生成的jsp木马文件压缩城成zip文件&#xff0c;然后再修改zip后缀文war 然后进行上传 使用哥斯拉进行测试连接 CVE-…

基于SpringBoot+Vue的在线酒店预订系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

微服务——网关登录校验(一)

1.网关登录校验 微服务中的网关登录校验是微服务架构中常见的一种安全机制&#xff0c;用于在请求到达微服务之前&#xff0c;对用户的身份进行验证&#xff0c;确保只有合法的用户才能访问相应的服务。 在微服务架构中&#xff0c;每个微服务都是独立部署的&#xff0c;它们之…

Apipost IDEA插件新升级,Apipost Helper上架IDEA插件市场

大家好&#xff01;今天向大家介绍一个非常方便的IDEA插件——Apipost Helper&#xff01;相信很多使用过Apipost的朋友在开发过程中都希望能够直接将编写好的API同步至Apipost&#xff0c;而无需手动填写。前段时间&#xff0c;Apipost推出了Apipost IDEA插件的内测版&#xf…

macOS平台编译libidn2库给iOS及macOS用

1.克隆源码: git clone https://gitlab.com/libidn/libidn2.git --recursive 2.安装依赖库: pkg-config也要安装 3.启动bootstrap生成configure 配置成功 configure生成成功

概率论与数理统计(2)

第一节博客已经整理了求导的公式&#xff0c;一些常用的概念。链接如下&#xff1a;高等数学基础&#xff08;1&#xff09;-CSDN博客。 第二节博客整理了微积分的公式及其相关概念。链接如下&#xff1a;高等数学基础&#xff08;2&#xff09;——微积分-CSDN博客 第三节博客…

主流卷积神经网络CNN总结

ResNet&#xff08;2015&#xff09;残差神经网络 残差结构 ResNet50具体卷积结构图 ResNeXt&#xff08;2016&#xff09;加入了分组卷积的思想&#xff0c;将原ResNet网络中的block替换成由group分组的block&#xff0c;两者得到的feature map一致&#xff0c;只是参数量更少…

计算机网络(月考一知识点)

文章目录 计算机网络背诵默写版计算机网络知识点&#xff08;月考1版&#xff09; 计算机网络背诵默写版 为我自己留个印记&#xff0c;本来荧光笔画的是没记住的&#xff0c;但是后面用紫色的&#xff0c;结果扫描的时候就看不见了。 计算机网络知识点&#xff08;月考1版&a…