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

上一篇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.// 实例化这个beanBeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {// 如果是单例,则从factoryBeanInstanceCache获取一个FactoryBeanWrapper对象,// 默认情况是单且factoryBeanInstanceCache为空,所以instanceWrapper还是 = nullinstanceWrapper = 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.// 允许后置处理器修改beanDefinitionsynchronized (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 提供了通过工厂方法来实例化Beanif (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.mzph.cn/web/43205.shtml

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

相关文章

MT3046 愤怒的象棚

思路&#xff1a; a[]存愤怒值&#xff1b;b[i]存以i结尾的&#xff0c;窗口里的最大值&#xff1b;c[i]存以i结尾的&#xff0c;窗口里面包含✳的最大值。 &#xff08;✳为新大象的位置&#xff09; 例&#xff1a;1 2 3 4 ✳ 5 6 7 8 9 则ans的计算公式b3b4c4c5c6b7b8b9…

【记录】LaTex|LaTex 代码片段 Listings 添加带圆圈数字标号的箭头(又名 LaTex Tikz 库画箭头的简要介绍)

文章目录 前言注意事项1 Tikz 的调用方法&#xff1a;newcommand2 标号圆圈数字的添加方式&#xff1a;\large{\textcircled{\small{1}}}\normalsize3 快速掌握 Tikz 箭头写法&#xff1a;插入点相对位移标号node3.1 第一张图&#xff1a;插入点相对位移3.2 第二张图&#xff1…

【MindSpore学习打卡】应用实践-LLM原理和实践-基于MindSpore实现BERT对话情绪识别

在当今的自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情绪识别是一个非常重要的应用场景。无论是在智能客服、社交媒体分析&#xff0c;还是在情感计算领域&#xff0c;准确地识别用户的情绪都能够极大地提升用户体验和系统的智能化水平。BERT&#xff08;Bidirec…

imx6ull/linux应用编程学习(12)CAN应用编程基础

关于裸机的can通信&#xff0c;会在其他文章发&#xff0c;这里主要讲讲linux上的can通信。 与I2C,SPI等同步通讯方式不同&#xff0c;CAN通讯是异步通讯&#xff0c;也就是没有时钟信号线来保持信号接收同步&#xff0c;也就是所说的半双工&#xff0c;无法同时发送与接收&…

C++基础篇(1)

目录 前言 1.第一个C程序 2.命名空间 2.1概念理解 2.2namespace 的价值 2.3 namespace的定义 3.命名空间的使用 4.C的输入输出 结束语 前言 本节我们将正式进入C基础的学习&#xff0c;话不多说&#xff0c;直接上货&#xff01;&#xff01;&#xff01; 1.第一个C程…

【Linux进阶】文件系统8——硬链接和符号连接:ln

在Linux下面的链接文件有两种&#xff0c; 一种是类似Windows的快捷方式功能的文件&#xff0c;可以让你快速地链接到目标文件&#xff08;或目录)&#xff1b;另一种则是通过文件系统的inode 链接来产生新文件名&#xff0c;而不是产生新文件&#xff0c;这种称为硬链接&…

base SAS programming学习笔记10(combine data)

1.一对一合并 基本格式如下&#xff1a; data output-data-set; set data-set1; set data-set2;(data-set1和data-set2可以是相同的数据集&#xff0c;可以添加多个set 语句来实现上述的一对一合并) run; 输出数据集结果如下&#xff1a; a.会包含所有输入数据的变量名&#x…

小米手机永久删除的照片怎么找回?这两个方法千万不要错过!

小米手机永久删除的照片怎么找回&#xff1f;身为米粉发烧党的小编又双叒叕手残了&#xff01;本来想在手机回收站中恢复一张照片&#xff0c;结果一个稀里糊涂就把照片点成了“永久删除”。于是乎难得的休班假期&#xff0c;就变成了小编恢复永久删除照片的漫漫之路。以下是小…

org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXXXX的作用是什么?

org.springframework.boot.autoconfigure.EnableAutoConfigurationXXXXXXX 这一配置项在 Spring Boot 项目中的作用如下&#xff1a; 自动配置类的指定&#xff1a; 这一配置将 EnableAutoConfiguration 设置为 cn.geek.javadatamanage.config.DataManageAutoConfiguration&…

【2024_CUMCM】TOPSIS法(优劣解距离法)

目录 引入 层次分析法的局限性 简介 例子 想法1 想法2 运用实际分数进行处理 想法3 问题 扩展问题&#xff1a;增加指标个数 极大型指标与极小型指标 统一指标类型-指标正向化 标准化处理 计算公式 计算得分 对原公式进行变化 升级到m个指标和n个对象 代码 …

系统分析师-基础知识

基础知识 一、计算机组成与结构1、计算机系统基础知识1.1 计算机硬件组成1.2 中央处理单元&#xff08;CPU&#xff09;1.3 数据表示1.3.1 R进制转十进制&#xff1a;1.3.2 十进制转R进制&#xff1a; 1.4 校验码&#xff08;3种校验码&#xff09;1.4.1 基本知识1.4.2 奇偶校验…

D-DPCC: Deep Dynamic Point Cloud Compression via 3D Motion Prediction

1. 论文基本信息 发布于&#xff1a; 2022 2. 创新点 首先提出了一种端到端深度动态点云压缩框架(D-DPCC)&#xff0c;用于运动估计、运动补偿、运动压缩和残差压缩的联合优化。提出了一种新的多尺度运动融合(MMF)模块用于点云帧间预测&#xff0c;该模块提取和融合不同运动流…

首届UTON区块链开发者计划大会在马来西亚圆满落幕

7月9日&#xff0c;首届UTON区块链开发者计划大会在马来西亚吉隆坡成功举办&#xff01; 来自全球顶尖的行业领袖、技术精英和众多区块链爱好者参与了此次盛会&#xff0c;也标志着UTON区块链生态进入了一个全新的发展阶段。 会上&#xff0c;UTON区块链创始人之一唐毅先生以“…

Python 中什么是递归函数,如何编写递归函数?

递归是计算机科学中的一种基本概念&#xff0c;它指的是函数调用自身的编程技巧。在Python中&#xff0c;递归函数是一种通过调用自身来解决问题的函数。这种方法常用于解决可以被分解为较小相同问题的场景&#xff0c;例如阶乘计算、斐波那契数列、全排列生成等。 一、递归的…

TCP 握手数据流

这张图详细描述了 TCP 握手过程中&#xff0c;从客户端发送 SYN 包到服务器最终建立连接的整个数据流转过程&#xff0c;包括网卡、内核、进程中的各个环节。下面对每个步骤进行详细解释&#xff1a; 客户端到服务器的初始连接请求 客户端发送 SYN 包&#xff1a; 客户端发起…

添加点击跳转页面,优化登录和注册页路由

一、给注销按钮添加点击跳转至登录页 1、在路由中添加登录页路由 2、自定义登录页面 3、在app.vue页面找到下拉框组件&#xff0c;添加点击事件 4、使用vue-router中的useRoute和useRouter 点击后可以跳转&#xff0c;但是还存在问题&#xff0c;路径这里如果我们需要更改登录…

JavaScript 作用域 与 var、let、const关键字

目录 一、JavaScript 作用域 1、全局作用域 2、函数作用域 3、块级作用域 4、综合示例 5、总结 二、var、let、const 1、var 关键字 2、let 关键字 3、const 关键字 4、总结 5、使用场景 一、JavaScript 作用域 在JavaScript中&#xff0c;作用域是指程序中可访问…

神经网络构成、优化、常用函数+激活函数

Iris分类 数据集介绍&#xff0c;共有数据150组&#xff0c;每组包括长宽等4个输入特征&#xff0c;同时给出输入特征对应的Iris类别&#xff0c;分别用0&#xff0c;1&#xff0c;2表示。 从sklearn包datasets读入数据集。 from sklearn import darasets from pandas impor…

vulnhub-NOOB-1

确认靶机 扫描靶机发现ftp Anonymous 的A大小写都可以 查看文件 解密 登录网页 点击about us会下载一个压缩包 使用工具提取 steghide info 目标文件 //查看隐藏信息 steghide extract -sf 目标文件 //提取隐藏的文件 steghide embed -cf 隐藏信息的文件 -ef…

什么样的开放式耳机好用舒服?南卡、倍思、Oladance高人气质量绝佳产品力荐!

​开放式耳机在如今社会中已经迅速成为大家购买耳机的新趋势&#xff0c;深受喜欢听歌和热爱运动的人群欢迎。当大家谈到佩戴的稳固性时&#xff0c;开放式耳机都会收到一致好评。对于热爱运动的人士而言&#xff0c;高品质的开放式耳机无疑是理想之选。特别是在近年来的一些骑…