Spring源码二十:Bean实例化流程三

news2024/11/14 12:21:48

上一篇Spring源码十九:Bean实例化流程二中,我们主要讨论了单例Bean创建对象的主要方法getSingleton了解到了他的核心流程无非是:通过一个简单工厂的getObject方法来实例化bean,当然spring在实例化前后提供了扩展如:beforeSingletonCreation与afterSingletonCreate,同样为了提供性能会将实例化后的单例bean放入缓存中;又因为spring设计之初存在三级缓存,所以在放入缓存的时候又会将其他两次的缓存清除。

简单的回忆了之前的内容,我们发现还有一个很重要的点我们没有说到那就是怎么通过简单工厂来创建实例对象的,这一篇咱们详细讨论一下:


createBean

	/**
	 * Central method of this class: creates a bean instance, 创建bean实例对象
	 * populates the bean instance, applies post-processors, etc. 填充bean实例、应用后置处理器
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		// 判断需要创建的bean是否可以实例化、是否可以通过当前类加载器加载
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		// 准备bean中的方法覆盖
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			// 给BeanPostProcessors一个返回代理而不是目标bean实例的机会。
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			// 如果bean配置类后置处理器PostProcessor,则这里返回一个proxy代理对象
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			// bean实例对象创建方法
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

因为lockup-method属性与repliace-methon配置属性,现在基本上没有使用场景,而resolveBeanClass与preprareMethodOverrides是为了是实现这个两个方法而生的,所以我们直接来看 resolveBeforeInstantiation方法。

resolveBeforeInstantiation


	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					// BeanPostProcessor前置处理方法
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						// BeanPostProcessor后置处理方法
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

InstantiationAwareBeanPostProcessorBeanPostProcessor 的一个子接口,提供了以下方法,用于在 Bean 实例化的不同阶段进行干预:

  • postProcessBeforeInstantiation(Class<?> beanClass, String beanName): 在 Bean 实例化之前调用。
  • postProcessAfterInstantiation(Object bean, String beanName): 在 Bean 实例化之后调用。
  • postProcessProperties(PropertyValues pvs, Object bean, String beanName): 在 Bean 的属性设置之前调用。

这些方法提供了在 Bean 实例化过程中进行自定义逻辑处理的机会,可以用于 Bean 的替换、属性的预处理等操作。

示例代码:

public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        if ("myBean".equals(beanName)) {
            System.out.println("Before instantiation of " + beanName);
            // 可以返回一个代理对象或自定义的 Bean 实例
        }
        return null; // 返回 null 表示继续默认的实例化过程
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) {
        System.out.println("After instantiation of " + beanName);
        return true; // 返回 true 表示继续进行属性设置
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        System.out.println("Processing properties for " + beanName);
        return pvs; // 可以修改属性值
    }
}

InstantiationAwareBeanPostProcessor 扩展了 BeanPostProcessor 的功能,提供了在 Bean 实例化的不同阶段进行干预的能力。通过实现 InstantiationAwareBeanPostProcessor 接口,开发人员可以在 Bean 实例化过程中插入自定义逻辑,实现更灵活的 Bean 管理和控制。

实例化与初始化

为了更好地理解 BeanPostProcessorInstantiationAwareBeanPostProcessor 的区别,我们需要明确实例化和初始化的概念。

  • 实例化:实例化是从零到一创建一个 Bean 的过程,即通过调用构造函数生成 Bean 的实例。
  • 初始化:初始化是在 Bean 实例化之后进行的配置过程,包括属性注入、调用初始化方法等。

在 Spring 容器中,Bean 的生命周期大致分为以下几个阶段:

  1. 实例化:通过调用构造函数创建 Bean 实例。
  2. 属性注入:将依赖的属性注入到 Bean 中。
  3. 初始化:调用自定义的初始化方法,进行额外的配置。

BeanPostProcessor 与 InstantiationAwareBeanPostProcessor 的区别

  • 作用阶段

    • BeanPostProcessor 主要作用于初始化阶段,即在 Bean 的属性已经注入之后进行处理。
    • InstantiationAwareBeanPostProcessor 作用于实例化阶段和属性注入阶段,允许在 Bean 实例化之前、之后以及属性注入之前进行处理。
  • 用途

    • BeanPostProcessor 常用于在 Bean 初始化之前和之后执行一些通用的处理逻辑,如代理增强、配置验证等。
    • InstantiationAwareBeanPostProcessor 常用于在 Bean 实例化过程中执行一些特殊的处理逻辑,如提前终止 Bean 创建、动态生成代理对象、修改属性注入逻辑等。

这个方法是在 Bean 初始化后应用所有注册的 BeanPostProcessorpostProcessAfterInitialization 方法,其设计和实现反映了 Spring 框架中依赖注入和面向切面编程的核心理念。

Spring Bean 生命周期管理

在 Spring 框架中,Bean 的生命周期经历了多个阶段,包括实例化、依赖注入、初始化和销毁等。applyBeanPostProcessorsAfterInitialization 方法所处的阶段是在 Bean 初始化之后,即在所有属性被设置后,执行自定义的后处理逻辑。

BeanPostProcessor 接口作用

BeanPostProcessor 接口定义了在 Bean 初始化前后可以插入自定义逻辑的能力。Spring 容器在创建 Bean 的过程中,会检查是否注册了 BeanPostProcessor,如果有,则会在相应的阶段调用其方法。其中,postProcessAfterInitialization 方法是在 Bean 初始化完成后被调用的,允许开发者对 Bean 进行额外的处理或修改。

applyBeanPostProcessorsAfterInitialization 方法分析

  1. 初始化结果对象:方法开始时,首先将 result 对象初始化为 existingBean,即当前的 Bean 实例。这个 existingBean 是在容器中已经完成初始化的对象。

  2. 遍历处理器列表:方法接着遍历所有注册的 BeanPostProcessor,对每一个后处理器调用其 postProcessAfterInitialization 方法。

    • 每个处理器可以在方法内部执行任何与 Bean 相关的操作,例如添加代理、执行验证、修改属性等。
    • 如果某个处理器返回 null,则表示不需要进一步处理,直接返回当前的 result 对象。
  3. 返回处理后的结果:最终返回经过所有后处理器处理后的 result 对象。这个对象可能是原始的 existingBean,也可能是经过多个处理器处理后的新对象。

技术原理分析

  • 面向切面编程(AOP)的应用:通过 BeanPostProcessor 接口,Spring 实现了 AOP 的一种简单形式。开发者可以在 Bean 初始化后插入切面逻辑,例如添加事务、日志等。

  • 依赖注入的增强:允许在 Bean 初始化后对依赖关系进行增强或修改,以适应不同的运行时需求。

  • 灵活性与可扩展性:applyBeanPostProcessorsAfterInitialization 方法展示了 Spring 框架在管理 Bean 生命周期时的高度灵活性和可扩展性。开发者可以通过注册自定义的 BeanPostProcessor 实现特定的业务逻辑,而不必修改现有的 Bean 实现代码。

applyBeanPostProcessorsAfterInitialization 方法在 Spring 框架中扮演了重要角色,通过它,开发者可以在 Bean 初始化完成后添加自定义逻辑,扩展和定制应用程序的行为。理解这个方法的工作原理和技术实现,有助于深入理解 Spring 容器的工作机制,提升对 Bean 生命周期管理的控制和应用开发的灵活性。

总结

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

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

相关文章

【堆 (优先队列) 扫描线】218. 天际线问题

本文涉及知识点 堆 &#xff08;优先队列) 扫描线 LeetCode218. 天际线问题 城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度&#xff0c;请返回 由这些建筑物形成的 天际线 。 每个建筑物的几何信息由数组 buildings 表示&…

来一场栈的大模拟(主要是单调栈)

一.栈模拟 二.单调栈求最大矩形面积 通常&#xff0c;直方图用于表示离散分布&#xff0c;例如&#xff0c;文本中字符的频率。 现在&#xff0c;请你计算在公共基线处对齐的直方图中最大矩形的面积。 图例右图显示了所描绘直方图的最大对齐矩形。 输入格式 输入包含几个测…

新火种AI|OpenAI的CEO又有新动作?这次他成立了AI健康公司

作者&#xff1a;一号 编辑&#xff1a;美美 AI技术即将改变医疗健康市场。 就在前两天&#xff0c;人工智能和医疗健康领域迎来了一个重要时刻。OpenAI的CEO萨姆阿尔特曼&#xff08;Sam Altman&#xff09;与Thrive Global的CEO阿里安娜赫芬顿&#xff08;Arianna Huffing…

oracle(表空间分类、表空间操作、默认表空间)

文章目录 oracle数据库默认表空间列表表空间是什么&#xff1f;表空间的分类1.永久性表空间&#xff1a;2.临时性表空间&#xff1a;3.撤销表空间&#xff1a; 表空间的作用Oracle 系统自动建立的表空间默认表空间1&#xff0e;SYSTEM 表空间2&#xff0e;SYSAUX表空间3&#x…

【STM32/HAL】嵌入式课程设计:简单的温室环境监测系统|DS18B20 、DHT11

前言 板子上的外设有限&#xff0c;加上想法也很局限&#xff0c;就用几个传感器实现了非常简单的监测&#xff0c;显示和效应也没用太复杂的效果。虽说很简单&#xff0c;但传感器驱动还是琢磨了不久&#xff0c;加上串口线坏了&#xff0c;调试了半天才发现不是代码错了而是…

Python大数据分析——决策树和随机森林

Python大数据分析——决策树和随机森林 决策树决策树节点字段的选择信息熵条件熵信息增益信息增益率 基尼指数条件基尼指数基尼指数增益 决策树函数 随机森林函数 决策树 图中的决策树呈现自顶向下的生长过程&#xff0c;深色的椭圆表示树的根节点&#xff1b;浅色的椭圆表示树…

降压转换器-从分立电路到完全集成的模块

降压转换器已存在了一个世纪&#xff0c;是当今电子电路中不可或缺的一部分。本文将讲述一个原始分立式器件如何演变成可以处理数百瓦功率的微型高集成器件。 降压转换器是将输入电压转换为较低的输出电压&#xff0c;基本原理如图 1所示。最初&#xff0c;开关 SW1 关断&…

设计模式之Facade设计模式

Facade设计模式&#xff0c;也称为外观模式&#xff0c;是一种结构型设计模式&#xff0c;它主要用于为子系统中的一组接口提供一个统一的高层接口&#xff0c;从而使得子系统更加容易使用。以下是关于Facade设计模式的详细介绍&#xff1a; 一、定义 Facade模式为多个复杂的…

数据库MySQL---基础篇

存储和管理数据的仓库 MySQL概述 数据库相关概念 数据库&#xff08;DataBase&#xff09;---数据存储的仓库&#xff0c;数据是有组织的进行存储 数据库管理系统&#xff08;DBMS&#xff09;-----操纵和管理数据库的大型软件 SQL----操作关系型数据库的编程语言&#xff…

C++之goto陈述

关键字 goto用于控制程式执行的顺序&#xff0c;使程式直接跳到指定标签(lable) 的地方继续执行。 形式如下 标签可以是任意的识别字&#xff0c;后面接一个冒号。 举例如下 #include <iostream>int main() {goto label_one;label_one: {std::cout << "Lab…

0302GPIO外设输入功能

GPIO外设输入功能 输入部分硬件电路按键简介传感器模块简介按键和传感器模块的硬件电路 C语言的学习C语言数据类型宏定义typedef结构体枚举C语言知识总结 按键控制LED灯&光敏传感器蜂鸣器GPIO总结GPIO使用方法总结模块化编程的方法&#xff1a; 两个程序&#xff1a;按键控…

Error:sql: expected 1 arguments, got 2

一 背景 在测试一个API接口时&#xff0c;看到日志里面突然抛出一个错误&#xff1a;Error:sql: expected 1 arguments, got 2 看了下&#xff0c;对应的表里面是有相关数据的&#xff0c;sql语句放在mysql里面执行也是没问题&#xff01;那奇了怪了&#xff0c;为啥会产生这样…

【MindSpore学习打卡】应用实践-热门LLM及其他AI应用-使用MindSpore实现K近邻算法对红酒数据集进行聚类分析

在机器学习领域&#xff0c;K近邻算法&#xff08;K-Nearest Neighbor, KNN&#xff09;是最基础且常用的算法之一。无论是分类任务还是回归任务&#xff0c;KNN都能通过简单直观的方式实现高效的预测。在这篇博客中&#xff0c;我们将基于MindSpore框架&#xff0c;使用KNN算法…

alibabacloud学习笔记11

讲解什么是配置中心及使用前后的好处 讲解Nacos作为配置中心面板介绍 官方文档 Nacos config alibaba/spring-cloud-alibaba Wiki GitHub 加入依赖&#xff1a; 订单服务和视频服务也加上这个依赖。 讲解Nacos作为配置中心实战 订单服务添加配置。 我们注释掉之前的配置。 …

Java项目:基于SSM框架实现的农家乐信息管理平台含前后台【ssm+B/S架构+源码+数据库+答辩PPT+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的农家乐信息管理平台 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

Mybatis Plus 3.X版本的insert填充自增id的IdType.ID_WORKER策略源码分析

总结/朱季谦 某天同事突然问我&#xff0c;你知道Mybatis Plus的insert方法&#xff0c;插入数据后自增id是如何自增的吗&#xff1f; 我愣了一下&#xff0c;脑海里只想到&#xff0c;当在POJO类的id设置一个自增策略后&#xff0c;例如TableId(value "id",type …

Linux多进程和多线程(八)多线程

多线程 线程定义线程与进程线程资源 线程相关命令 pidstat 命令 top 命令ps 命令常见的并发方案 1. 多进程模式2. 多线程模式 创建线程 1. pthread_create() 示例:创建一个线程 2. pthread_exit() 退出线程3. pthread_join() 等待线程结束 示例: 线程分离 创建多个线程 示例 1:…

Spring Boot集成grpc快速入门demo

1.什么是GRPC&#xff1f; gRPC 是一个高性能、开源、通用的RPC框架&#xff0c;由Google推出&#xff0c;基于HTTP2协议标准设计开发&#xff0c;默认采用Protocol Buffers数据序列化协议&#xff0c;支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务&#xff0c…

VUE之旅—day3

工程化开发和脚手架Vue CLI 开发Vue的两种方式&#xff1a; 核心包创痛开发模式&#xff1a;基于html/css/js文件&#xff0c;直接引入核心包&#xff0c;开发Vue。 工程化开发模式&#xff1a;基于构建工具&#xff08;例如&#xff1a;webpack&#xff09;的环境中开发Vue。…