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

上一篇我们在Spring源码十八:Bean实例化流程一 中,主要讨论了Spring在实例化前的两重要准备工作,1、获取我们前面注册好的BeanDefinition,将GenericBeanDefinition封装为RootBeanDefinition如果Bean Definition只存在父容器中,还会进行合并操作,然后做了严谨的异常判断处理。2、如果bean配置了依赖的bean的名称,还会检查下配置的依赖,是否已经处于bean依赖的引用链上了,如果没有处于bean依赖引用链上,就会提前来实例化bean依赖的那些bean。最后找到实例化的入口。

 今天我们开始分析下单例bean是如何创建:咱们接着上一篇代码往下看

getSingleton

再进入代码中看下逻辑:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {// 从单例缓存中获取Bean的实例Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {// 如果当前bean正在销毁标志为true,则抛出异常。 默认:singletonsCurrentlyInDestruction = falseif (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 将当前beanName放入singletonsCurrentlyInCreation列表中,标志当前bean正在被创建beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// !!!!  调用简单工厂方法来实例化bean  !!!! //singletonObject = singletonFactory.getObject();// 标志当前bean是第一次过来,默认是falsenewSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.// 再努力一次,如果通过简单工厂创建失败: 尝试从单例缓存中,获取beanName对应的单例beansingletonObject = this.singletonObjects.get(beanName);// 缓存里还是没有,此时再将异常抛出if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 从singletonsCurrentlyInCreation列表中移出,标志当前beanName对应的bean已经创建完成了。afterSingletonCreation(beanName);}if (newSingleton) {// 看到了嘛,看到了嘛,第一次标志在这里用的,单例缓存入的地方也再这里,哈哈 我们找到啦addSingleton(beanName, singletonObject);}}return singletonObject;}}

这段代码是Spring框架中用于创建和获取单例bean实例的一部分。以下是对这段代码的分析:

  1. 使用lambda表达式:代码中使用了Java 8的lambda表达式来创建一个匿名的 ObjectFactory 实例。这个 ObjectFactory 会在需要时调用其 getObject() 方法来创建bean。

  2. getSingleton()方法getSingleton(beanName, singletonFactory) 方法被调用来获取名为 beanName 的单例bean。如果这个bean尚未被创建,singletonFactory.getObject() 将被调用以创建它。

  3. createBean()方法:在lambda表达式中,如果bean不存在,将调用 createBean(beanName, mbd, args) 方法来创建bean。这个方法负责完整的bean创建过程,包括依赖注入、初始化等步骤。

  4. 异常处理:如果在创建bean的过程中抛出了 BeansException(例如,由于循环依赖或其他bean创建问题),代码将执行以下操作:

    • 调用 destroySingleton(beanName) 来从单例缓存中移除部分创建的bean实例。这是必要的,因为创建过程中可能会因为解决循环依赖而提前将bean放入缓存。
    • 抛出原始的 BeansException,以通知调用者bean创建失败。
  5. 获取bean实例:如果bean成功创建,sharedInstance 将包含新创建的bean实例。然后,调用 getObjectForBeanInstance(sharedInstance, name, beanName, mbd) 方法来获取bean实例对象。这个方法可能执行额外的处理,比如应用 FactoryBean 的逻辑或处理bean的后置处理(post-processing)。

  6. 单例缓存:整个过程中,Spring使用 singletonObjects 作为缓存来存储已经创建的单例bean。这样,后续请求相同bean时可以直接从缓存中获取,而不需要重新创建。

这段代码体现了Spring框架中创建单例bean的复杂性,包括异常安全、延迟初始化和对循环依赖的处理。通过使用 ObjectFactory 和lambda表达式,Spring能够以一种灵活且高效的方式来管理bean的生命周期。

createBean

上面我已经找到真正创建bean的地方,也看到Spring是通过简单工厂创建实例的也验证了人命常说的bean工厂、工厂模式。同时也看到单例bean首次创建,会放入单例缓存池中。但是整个方法有个内部类,当然这里是Java8的函数式接口实现的,咱们跳出来看下这个默认方法是做了什么逻辑处理。通过这个方法来

看到上图标注的地方createBean见名知意,应该是这个方法包含了实例化的核心逻辑,咱们进入方法内部一探究竟之前简单分析一下:在这个内部类里首先会通过createBean来实例化一个bena,如果实例化的过程中出现了异常,就会调用方法destroy Singleton方法,来清除单利bean相关的一系列缓存信息。

/*** Central method of this class: creates a bean instance, 创建bean实例对象* populates the bean instance, applies post-processors, etc. 填充bean实例、应用后置处理器* @see #doCreateBean*/@Overrideprotected 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 {Object beanInstance = doCreateBean(beanName, mbdToUse, args); // bean实例对象创建方法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);}}

 

总结

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

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

相关文章

.profile脚本

.profile 是一个用户级别的 shell 启动脚本&#xff0c;用于初始化用户环境设置&#xff0c;通常用于配置环境变量、设置路径、运行初始化命令等。以下是一个示例 if [ "$BASH" ]; thenif [ -f ~/.bashrc ]; then. ~/.bashrcfi fimesg n || true 这段代码通常出现在…

计算器原生js

目录 1.HTML 2.CSS 2.JS 4.资源 5.运行截图 6.下载连接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

【C++】引用变量详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

文档去重(TF-IDF,MinHash, SimHash)

2个doc有些相似有些不相似&#xff0c;如何衡量这个相似度&#xff1b; 直接用Jaccard距离&#xff0c;计算量太大 TF-IDF: TF*IDF TF&#xff1a;该词在该文档中的出现次数&#xff0c; IDF&#xff1a;该词在所有文档中的多少个文档出现是DF&#xff0c;lg(N/(1DF)) MinHash …

数据分析_计划

我做大数据的有6年了&#xff0c;以前都是用sql&#xff0c;或者spark&#xff0c;java&#xff0c;scala&#xff0c;python去做。现在这些平台搭建、维护、大多数都是搭建一次就完了&#xff0c;而且维护大多是大厂直接用云平台去做。ETL也是就做一次就够了&#xff0c;我们公…

基于JAVA+SpringBoot+Vue+Uni-app前后端分离的校园好物小红书分享平台小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 在快速数字化的时代背…

同三维T80004EA编解码器视频使用操作说明书:高清HDMI编解码器,高清SDI编解码器,4K超清HDMI编解码器,双路4K超高清编解码器

同三维T80004EA编解码器视频使用操作说明书&#xff1a;高清HDMI编解码器&#xff0c;高清SDI编解码器&#xff0c;4K超清HDMI编解码器&#xff0c;双路4K超高清编解码器 同三维T80004EA编解码器视频使用操作说明书&#xff1a;高清HDMI编解码器&#xff0c;高清SDI编解码器&am…

UniVue@v1.3.0版本发布

GitHub仓库 发布版本仓库&#xff1a;https://github.com/Avalon712/UniVue 开发版本仓库&#xff1a;https://github.com/Avalon712/UniVue-Develop UniVue拓展框架UniVue源生成器仓库&#xff1a;https://github.com/Avalon712/UniVue-SourceGenerator v1.3.0版本新增功能…

DangerWind-RPC-framework---四、SPI

SPI 即 Service Provider Interface &#xff0c;可以理解为专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。SPI 将服务接口和具体的服务实现分离开来&#xff0c;将服务调用方和服务实现者解耦&#xff0c;能够提升程序的扩展性、可维护性。修改或者替换服务…

etcd 实现分布式锁

10 基于 Etcd 的分布式锁实现原理及方案

如何通过兔子和窝窝的故事理解“在机器人学习和研究中的获得成本与维护成本”(节选)

获得成本 掌握一门课程&#xff0c;以最为简单的学校成绩过60为例&#xff0c;需要按要求提交材料&#xff0c;包括作业、报告、实验和考试等&#xff0c;依据学分和考核要求的不同&#xff0c;需要对于花费时间和经历进行完成。 维护成本 考完了&#xff0c;如果被动学习那…

docker拉取镜像-配置阿里云镜像加速

1、配置阿里云镜像&#xff08;用于拉取镜像加速&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo syst…

Docker 使用基础(4)—存储卷

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;秒針を噛む—ずっと真夜中でいいのに。 0:34━━━━━━️&#x1f49f;──────── 4:20 &#x1f504; ◀️ ⏸ …

JVM堆内存的结构,YGC,FGC的原理

JVM堆内存结构&#xff1a; JVM堆内存可分为三个区域&#xff1a;新生代&#xff08;Young Generation&#xff09;、年老代&#xff08;Tenured Generation&#xff0c;也叫做Old Generation&#xff09;和永久代&#xff08;Permanent Generation&#xff0c;也叫做Method Ar…

linux 设置nginx开机自启

1、关闭当前nginx运行 systemctl stop nginx 2、添加以下内容到nginx.service文件&#xff0c;注意nginx.pid文件的路径&#xff0c;要替换哦&#xff01; vim /etc/systemd/system/nginx.service [Unit] DescriptionThe NGINX HTTP and reverse proxy server Afternetwork…

ArcGIS如何快速对齐两个图层

1、问题 如何让两个图层快速对齐 2、使用捕捉工具 移动点或折点&#xff0c;使其与其他要素的折点、边或端点精确重合。 可指定捕捉规则来控制是将输入折点捕捉到指定距离范围内的最近折点、边还是端点。

MySQL数字相关数据处理函数

目录 1. 随机数生成 rand ( ) 2. 四舍五入 round&#xff08;&#xff09; 3. 舍去 truncate ( ) 4. 向上/下取整 5. 空处理 ifnull&#xff08; x , y &#xff09; 1. 随机数生成 rand ( ) rand ( ) 生成 0 到 1 的随机数&#xff1b; rand ( x ) 生成 0 到 1 的随机数…

简单理解Lua 协程(coroutine)

也许更好的阅读体验 协程简单理解为可以暂停的线程&#xff0c;但是同一时刻只有一个协程可以处于运行状态。 文章目录 coroutine.create()coroutine.resume()coroutine.wrap()coroutine.yield()coroutine.resume()参数传递resume和yield之间互换数据 coroutine.create() lua…

403 禁止错误: 它是什么?如何修复?

您应该对403错误代码很熟悉&#xff01;这种错误会导致流量损失&#xff0c;甚至错失一些商业机会&#xff01; 什么&#xff1f;您在自己的网站上遇到了403错误&#xff1f;请立即修复它&#xff01;但是什么原因导致这种错误&#xff1f;该如何解决&#xff1f;这两个问题都…

66种智能优化算法和改进优化算法优化BP神经网络【开源代码!】【文末福利IT学习资料】

前言 熟话说得好&#xff0c;创新点不够&#xff0c;智能优化算法来凑&#xff0c;不要觉得羞耻&#xff0c;因为不仅我们这么干&#xff0c;很多外国人也这么干&#xff01;因为创新点实在太难想了&#xff0c;和优化算法结合下是最简单的创新点了&#xff01; 之前给大家分享…