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

news2024/9/22 21:32:37

上一篇Spring源码二十:Bean实例化流程三中,我们主要讨论了单例Bean创建对象的主要方法getSingleton的内部方法createBean,createBean方法中的resolveBeanClase方法与prepareMethodOverrides方法处理了lookup-method属性与repliace-method配置属性。重点讨论了resolveBeforeInstantiation,看了Spring为我们提供的两个扩展方法。最后我们找到了正常创建bena的方法,doCreateBean。

今天开始进入doCreateBean中一探究竟:


doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		// 实例化这个bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			// 如果是单例,则从factoryBeanInstanceCache获取一个FactoryBeanWrapper对象,
			// 默认情况是单且factoryBeanInstanceCache为空,所以instanceWrapper还是 = null
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 创建BeanWrapper实例对象:默认情况都是空,所以肯定会走这里
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// 获取实例化对象
		final Object bean = instanceWrapper.getWrappedInstance();
		// 获取实例化对象类型
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		// 允许后置处理器修改beanDefinition
		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;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like 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));
		}

		// Initialize the bean instance. bean对象的初始化、DI在此出发
		// 这个exposed的对象,
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

首先回去BeanFactory的缓存中获取Bean Wrapper对象,默认没有所以肯定会走到createBeanInstance方法中,我们进入方法内部看下。

createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 在此调用resolveBeanClass方法,解析BeanDefinition中的class ,确保现在bean的class已经被解析好了
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		// 这里对class进行校验,主要是反射需要用的到的属性:
		// 如果类不是public修饰的,并且不能通过反射访问 提前抛出异常
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		// 如果属性factory-method不为空,则通过配置好的工厂方法来实例化bean
		// 这里为Spring 提供了通过工厂方法来实例化Bean
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		boolean resolved = false;
		boolean autowireNecessary = false;
		// 参数为空时处理
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

上述代码注释基本上都解释了一遍,咱们简单总结下:

类解析和校验:首先解析bean的类,并进行访问权限校验。这一步确保了后续操作的前提条件都已经满足。

实例供应商检查:如果定义了实例供应商,则通过供应商创建实例。这种方式提供了高度的灵活性,使得实例的创建可以由外部逻辑控制。一般不做扩展,故不做具体分析

工厂方法实例化:如果定义了工厂方法,则通过工厂方法创建实例。工厂方法模式是一种常见的设计模式,能够灵活地创建对象。这块我们等会进去看下。主要还是Spring给我们提供了一种实例化方式,等会举例说明。

构造函数解析和自动装配:解析候选的构造函数并进行自动装配。这种方式适用于需要依赖注入的场景,确保bean的依赖能够被正确地解析和注入。

默认实例化:如果没有特殊的创建需求,则使用无参构造函数进行实例化。这种方式简单直接,适用于大多数情况。

这段代码体现了Spring框架在设计和实现上的许多优秀思想和实践。通过灵活的实例化策略、严格的前置校验、线程安全处理、性能优化以及分层设计,Spring框架能够高效、稳定地处理复杂的bean创建需求。这些设计思想和实践对于任何大型软件系统的设计和实现都有重要的借鉴意义。

工厂方法来实例化Bean

有两种方式来定义,主要是静态方法与普通方法,下面咱们来看下代码:

package org.springframework.factory;

import org.springframework.dto.JmUser;

/**
 * @author Jeremy
 * @version 1.0
 * @description: 测试FactoryMethod方法
 * @date 2024/7/10 16:56
 */
public class JmUserFactory {
	public static JmUser getObject(){
		JmUser jmUser = new JmUser();
		jmUser.setName("JmUserFactory");
		jmUser.setAge("1");
		return 	jmUser;
	}
	public JmUser getJmUser(){
		JmUser jmUser = new JmUser();
		jmUser.setName("JmUserFactoryNoStatic");
		jmUser.setAge("2");
		return 	jmUser;
	}

}


package org.springframework.dto;

/**
 * @author Jeremy
 * @version 1.0
 * @description: 实体类
 * @date 2024/6/30 03:02
 */
public class JmUser {
	private String name;

	private String age;
	private String systemOs;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}
}
<!--factory-method 静态方法-->
	<bean id="jmUserFactory" class="org.springframework.factory.JmUserFactory" factory-method="getObject"></bean>

	<!--factory-method 非静态方法-->
	<bean id="jmUserFactoryNoStatic" class="org.springframework.factory.JmUserFactory" ></bean>
	<bean id="jmCreateUser" factory-bean="jmUserFactoryNoStatic" factory-method="getJmUser"  ></bean>

可以看到此时会我们在xml中设置的factory-method方法会set到BeanDefinitionfactoryMethodName中,这里通过getFactoryMethodName方法获取属性值,如果有值则进入factoryMethod方法内部进行实例化。

instantiateUsingFactoryMethod

代码比较长,我大家可以自己去代码里看,我这里就截取核心内容出来。

Spring 框架中用于通过工厂方法实例化 bean 的方法。无论是 bean 类上的静态工厂方法,还是其他工厂 bean 上的方法,都是通过这个方法来处理的。以下是该方法的概述和关键部分的解释:

instantiateUsingFactoryMethod 方法旨在使用指定的工厂方法创建 bean 的新实例。它通过解析适当的方法、准备必要的参数,然后调用该方法来创建 bean。

BeanWrapper 初始化:


BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);

这里创建了一个 BeanWrapperImpl 实例并进行初始化,BeanWrapper 用于包装 bean 对象并提供对其属性的操作。

获取工厂 bean 和工厂类:


String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
    if (factoryBeanName.equals(beanName)) {
        throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                "factory-bean reference points back to the same bean definition");
    }
    factoryBean = this.beanFactory.getBean(factoryBeanName);
    if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
        throw new ImplicitlyAppearedSingletonException();
    }
    factoryClass = factoryBean.getClass();
    isStatic = false;
}
else {
    if (!mbd.hasBeanClass()) {
        throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                "bean definition declares neither a bean class nor a factory-bean reference");
    }
    factoryBean = null;
    factoryClass = mbd.getBeanClass();
    isStatic = true;
}

这里获取工厂 bean 和工厂类。如果指定了工厂 bean 名称,则获取该工厂 bean 实例及其类;如果没有指定工厂 bean 名称,则使用 bean 类上的静态工厂方法。

解析工厂方法和参数:


Method factoryMethodToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;

if (explicitArgs != null) {
    argsToUse = explicitArgs;
}
else {
    Object[] argsToResolve = null;
    synchronized (mbd.constructorArgumentLock) {
        factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
        if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
            argsToUse = mbd.resolvedConstructorArguments;
            if (argsToUse == null) {
                argsToResolve = mbd.preparedConstructorArguments;
            }
        }
    }
    if (argsToResolve != null) {
        argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
    }
}

这里解析工厂方法和参数。如果有显式参数,则直接使用;否则,从 bean 定义中解析参数。

确定工厂方法:


if (factoryMethodToUse == null || argsToUse == null) {
    factoryClass = ClassUtils.getUserClass(factoryClass);
    List<Method> candidates = null;
    if (mbd.isFactoryMethodUnique) {
        if (factoryMethodToUse == null) {
            factoryMethodToUse = mbd.getResolvedFactoryMethod();
        }
        if (factoryMethodToUse != null) {
            candidates = Collections.singletonList(factoryMethodToUse);
        }
    }
    if (candidates == null) {
        candidates = new ArrayList<>();
        Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
        for (Method candidate : rawCandidates) {
            if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
                candidates.add(candidate);
            }
        }
    }
    ...
}

这里尝试确定具体的工厂方法。首先检查是否有缓存的工厂方法,然后尝试从工厂类的所有方法中找到匹配的方法。

实例化 bean:


bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
return bw;

最后,通过解析得到的工厂方法和参数来实例化 bean,并将其包装在 BeanWrapper 中返回。

总结下:就是判断一下当前是通过静态工厂方法,还是通过实例工厂方法来实例化bean的实例。剩下复杂的错都通过反射实例化bean的过程。

小结

今天咱们主要分析里createBeanInstance方法Spring给我们提供给的FactoryMethod方法,举例说明了factoryMethod属性如何使用,同时简单讨论了具体实现逻辑。下一节咱们将进入反射实例化Bean。

总结

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

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

相关文章

设计模式之外观模式(Facade)

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

TTT架构超越Transformer,ML模型替代RNN隐藏状态!

目录 01 算法原理 02 骨干架构 03 实验结果 一种崭新的大语言模型&#xff08;LLM&#xff09;架构有望取代当前主导 AI 领域的 Transformer&#xff0c;并在性能上超越 Mamba。 论文地址&#xff1a;https://arxiv.org/abs/2407.04620 本周一&#xff0c;关于 Test-Time Tr…

ur5e机械臂末端添加dh_ag95夹爪(ubuntu20.04+ROSnoetic)

一、从官网上下载UR5e机械臂 mkdir -p catkin_ws cd catkin_ws git clone https://github.com/UniversalRobots/Universal_Robots_ROS_Driver.git src/Universal_Robots_ROS_Driver git clone -b calibration_devel https://github.com/fmauch/universal_robot.git src/fmauch…

Vue3入门之创建vue3的单页应用(vite+vue)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

电机学-绪论

绪论 电机&#xff1a;根据电磁感应定律和电磁力定律实现机电能量转换和信号传递与转换的电磁机械装置。 电磁感应定律&#xff1a; BiliBili: 法拉第电磁感应定律 BiliBili: 楞次定律 BiliBili: 左手定则、右手定则、右手螺旋定则

GOLLIE : ANNOTATION GUIDELINES IMPROVE ZERO-SHOT INFORMATION-EXTRACTION

文章目录 题目摘要引言方法实验消融 题目 Gollie&#xff1a;注释指南改进零样本信息提取 论文地址&#xff1a;https://arxiv.org/abs/2310.03668 摘要 大型语言模型 (LLM) 与指令调优相结合&#xff0c;在泛化到未见过的任务时取得了重大进展。然而&#xff0c;它们在信息提…

【昇思25天学习打卡营打卡指南-第十八天】基于MobileNetv2的垃圾分类

基于MobileNetv2的垃圾分类 MobileNetv2模型原理介绍 MobileNet网络是由Google团队于2017年提出的专注于移动端、嵌入式或IoT设备的轻量级CNN网络&#xff0c;相比于传统的卷积神经网络&#xff0c;MobileNet网络使用深度可分离卷积&#xff08;Depthwise Separable Convolut…

玩机社区系统源码 | 2024年最美社区源码 全开源 带后端

简介&#xff1a; 玩机社区系统源码 | 2024年最美社区源码 全开源 带后端 图片&#xff1a; 点击下载

“Pandas数据处理与分析:实用技巧与应用“

目录 # 开篇 1. pandas的series的了解 1.1 pd.Series 创建 1.2 pd.series 的索引使用 1.3 pd.series 之字典/索引 1.4 pandas 转换数据类型 1.5 pandas 通过索引或者通过位置来取值 1.6 pandas 指定行取值 1.7 pands之Series 切片和索引 1.8 pands之Series 的索引和值…

火热夏季:浦语*书生InternLM大模型实战闯关-入门岛之Linux基础知识

一、ssh链接与端口映射并运行hello_wold.py 1.创建开发机 InternStudio创建开发机 2.进入开发机 3.Ssh链接开发机 powerShell终端ssh链接开发机。 4.创建一个hello_world.py文件web demo 5.运行web demo 6.端口映射 7.本地浏览器打开web 二、 VSCODE 远程连接开发机并创建一个…

Mac 上安转文字转 SQL 利器 WrenAI

WrenAI 是一个开源的 Text-SQL 的工具&#xff0c;通过导入数据库结构&#xff0c;通过提问的方式生成 SQL。本文将讲述如何在 MacOS 上安装 WrenAI。要运行WrenAI&#xff0c;首先需要安装 Docker 桌面版。 下载 WrenAI https://github.com/Canner/WrenAI/releases/tag/0.7.…

Spring Boot 高级配置:如何轻松定义和读取自定义配置

目录 1. 环境准备 2. 读取配置数据 2.1 使用 Value注解 2.2 Environment对象 2.3.2.3 自定义对象 这篇博客我们将深入探讨如何在Spring Boot应用中有效地定义和读取自定义配置。掌握这一技巧对于任何希望优化和维护其应用配置的开发者来说都是至关重要的。我们将从基础开始…

HTTPS理解

一个完整的HTTP连接 TCP三次握手接受窗口发送数据关闭连接 接受窗口是用来做什么呢&#xff1f; 它根据自身网络情况设置不同大小的值用来控制对方发送速度&#xff0c;避免对方发送太快&#xff0c;导致网络拥塞。 为什么TCP握手要三次&#xff1f; 1&#xff09;确认双方的…

镭速Raysync vs MASV:哪个才最合适企业大文件传输

在当前信息爆炸的时代&#xff0c;企业面临的一个关键挑战是如何高效、安全地传输日益增长的大量文件。选择正确的文件传输工具对于企业的日常运作至关重要。本文旨在对比分析两款备受瞩目的企业级大文件传输解决方案——镭速Raysync和MASV&#xff0c;以助企业决策者挑选出最适…

图像中高频信息、低频信息与ComfyUI中图像细节保留的简单研究

&#x1f9f5;背景 在做AI绘图的时候&#xff0c;经常有一些图像的细节需要保留原始图像内容&#xff0c;比如说衣服的细节&#xff0c;商品的文字标签等等&#xff0c;如果这些地方发生了变化&#xff0c;就会导致生成的结果无法直接商用&#xff0c;而让生成的图像完全保留原…

加速你的下载,IDM神器不可错过!快如闪电,稳如老狗

嗨&#xff0c;各位小伙伴&#xff01;&#x1f44b;&#x1f44b;&#x1f44b; 今天我要安利一个让你的下载体验起飞的神奇工具——Internet Download Manager&#xff08;简称IDM&#xff09;&#x1f6eb;。想象一下网速慢得像蜗牛爬的场景&#xff0c;是不是让人抓狂&…

3个方法教你如果快速绕过Excel工作表保护密码

在日常生活中&#xff0c;我们可能会遇到一些特殊情况&#xff0c;比如不小心忘记了Excel文件中设置的打开密码。别担心&#xff01;这里为您带来一份详细的Excel文件密码移除教程&#xff0c;助您轻松绕过Excel工作表保护。 方法一&#xff1a;使用备份文件 如果您有文件的备…

24暑假计划

暑假计划&#xff1a; 1.从明天起开始将C语言的部分补充完整&#xff0c;这部分的预计在7月24日前完成 2.由于之前的文章内容冗余&#xff0c;接下来进行C语言数据结构的重新编写和后面内容的补充预计8月10号前完成 3.后续开始C的初级学习

【热梗案例】知识点阶段性综合汇总

文章目录 渲染对象、实现统计功能实现删除功能设置发布按钮实现发布按钮的提交功能 直接用CSS的模板&#xff0c;模板代码如下&#xff1a; <template><view class"title">近期热梗</view><view class"out"> <view class&qu…

SuperCLUE最新测评发布,360智脑大模型稳居大模型第一梯队

7月9日&#xff0c;国内权威大模型评测机构SuperCLUE发布《中文大模型基准测评2024上半年报告》&#xff0c;360智脑大模型&#xff08;360gpt2-pro&#xff09;在SuperCLUE基准6月测评中&#xff0c;取得总分72分&#xff0c;超过GPT-3.5-Turbo-0125&#xff0c;位列国内大模型…