Spring框架系列之bean的生命周期底层原理08

2023-05-16

接着上一篇,咱们继续doCreateBean方法的分析,doCreateBean内容比较多,我们这次主要是把它的整体流程说下,后续会逐个来分析每一个关键点。代码如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }

   //关键点一
   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.
   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;
      }
   }

   //关键点三
   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));  // AService
   }
 
   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 " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }
 
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

关键点一

首次获取单例classA对象(续接“Spring框架系列之bean的生命周期底层原理07”的例子)肯定为空,这里会调用createBeanInstance实例化对象,何为实例化不用我多说了吧?就是new一个对象,既然是new一个对象,那如果ClassA这个类有多个构造函数咋办?Spring怎么知道调用哪个构造函数呢?所以这里Spring肯定需要有一个过程,用以推断具体的构造函数,这块我们放下一篇来详细展开。这里朋友们只要知道createBeanInstance方法就是通过new来得到一个对象,当然这里Spring是返回一个BeanWrapper类型的包装对象,对原始对象进行了封装。

关键点二

对象创建了以后,按照我们正常的java开发流程该做什么事情呢?是不是应该给对象的相关属性进行赋值了?Spring也是这么做的,但是Spring要明确知道,哪些属性是需要赋值的,这些属性赋值的方式是啥?是方法赋值还是属性赋值?

在Spring中有两个常见的注解是用来指定哪些属性是需要被赋值的,如Autowired、Value,当然还包括Inject,另外还有Resource等。在创建完成对象后,会调用
applyMergedBeanDefinitionPostProcessors方法来查找注入点,这些注入点就是被Autowired、Value、Resource这些注解标识的属性或者方法,具体怎么找的,我们放到后续篇幅详细分析,这里我们只需要知道通过applyMergedBeanDefinitionPostProcessors方法就能够找到具体的注入点,Spring将基于这些注入点进行赋值操作。

关键点三

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));

如上代码段,标识如果当前创建的是单例bean,并且允许循环依赖,并且还在创建过程中,那么则需要提早暴露,默认情况下allowCircularReferences是为true的,也就是说Spring默认情况下是允许循环依赖的,什么是循环依赖?就是就是我们的案例,ClassA依赖ClassB,ClassB又依赖ClassA。而且当前beanName实例肯定是在创建中的,所以这里earlySingletonExposure的值是true,于是Spring调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),注意这里的第二个参数是一个lambda表达式,addSingletonFactory方法代码如下:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
      this.singletonFactories.put(beanName, singletonFactory);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}
}

该方法将lambda表达式添加到singletonFactories集合中,lambda表达式的执行代码如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {  
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   return exposedObject;
}

会执行SmartInstantiationAwareBeanPostProcessor后置处理器的getEarlyBeanReference方法,大部分情况下getEarlyBeanReference返回的是原bean对象,除非AOP处理的情况下,这块我们后续讲AOP的时候详细分析。

关键点四

在找到注入点后,Spring接下来就是调用populateBean方法给属性赋值了,比如classA,需要给属性ClassB实例对象赋值,既然要赋值,那咱首先得有这个ClassB的实例对吧?但是此时Spring里面还没有ClassB这个类型的对象,所以在populateBean方法中又会进入ClassB对象的生命周期,来想下,当创建完ClassB的实例对象,也需要给ClassB对象的属性,也就是classA赋值,但是此时classA还不是一个健全的对象,于是出现了循环依赖问题,那Spring如何解决这个问题的呢?比较复杂,咱们后续会有专门篇幅分析。

关键点五

属性赋值完成后,调用initializeBean完成对象的初始化,以前笔者不是很明白初始化和实例化到底有啥区别,现在看来其实很简单,实例化就是JVM创建一个对象,而初始化则是需要结合实际的业务来看,更多的是业务层面的初始化操作。这里initializeBean方法其实执行了很多操作,比如执行Aware、初始化之前、初始化、初始化之后,具体我们后续篇幅分析。

关键点六

exposedObject = initializeBean(beanName, exposedObject, mbd),因为initializeBean方法中调用了后置处理器进行了处理,所以通过这个方法得到的exposedObject有可能已经被咱们自己增加的后置处理器修改了,如果咱们自己修改了,Spring认为我们的优先级会更高点,所有会使用咱们自己的,如果exposedObject == bean那么表示咱们自己没有修改bean对象,于是就使用Object earlySingletonReference = getSingleton(beanName, false)这个方法返回的值.

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring框架系列之bean的生命周期底层原理08 的相关文章

  • 航空订票系统设计(java、数据库、通信联合项目)

    航空订票系统设计 xff08 java 数据库 通信联合项目 xff09 最近帮高中同学做的一个学校项目 xff0c 这个项目主要是用Java写的 前期主要搭建五个类 xff0c Order xff08 选择 xff09 Passenger
  • java基础之IO流中的字节流

    目录 一 xff1a 字节流写数据 二 xff1a 字节流写数据的方式 三 xff1a 字节流写数据实例 字节流写数据步骤 字节流写数据例子 字节流追加写数据 四 xff1a 字节流读取数据 五 xff1a 字节读取数据的方法 六 xff1
  • java基础之转换流

    目录 一 xff1a 解释 二 xff1a 转换流输入流 1 xff1a 构造方法 2 xff1a InputStreamReader读数据方法 3 xff1a 例子 3 xff1a 注意 三 转换流输出流 1 xff1a 构造方法 2 x
  • java基础之多线程的引入

    目录 一 xff1a 多线程概述 进程 线程 举例 二 xff1a 并行与并发 三 xff1a Java程序的运行原理 四 xff1a JVM启动的时候是单线程还是多线程呢 xff1f 一 xff1a 多线程概述 进程 正在运行的程序 xf
  • 电信运营商云计算体系架构分析

    电信运营商云计算体系架构分析 作者 xff1a 成晓旭 xff08 版权保留 欢迎转载 xff09 第三篇 xff1a 体系架构分析 电信运营商云计算发展分析之一 xff1a 战略定位分析 xff0c 可供参考 电信运营商云计算发展分析之二
  • java基础之线程安全问题(一)

    目录 一 xff1a 线程安全判断依据 二 xff1a 解决线程安全问题实现 同步代码块 1 格式 2 同步代码块的锁对象是谁 xff1f 3 xff1a 同步方法的时候 xff0c 锁对象又是谁呢 xff1f 4 xff1a 静态同步方法
  • java基础之线程安全问题(二)

    目录 一 xff1a Lock锁的使用 xff08 解决同步问题 xff09 二 xff1a 例子 三 xff1a 同步的特点 xff08 1 xff09 同步的前提 xff08 2 xff09 同步的好处 xff08 3 xff09 同步
  • redis基础知识

    目录 一 xff1a 概念 二 xff1a NoSQL分类 三 xff1a Redis数据模型 四 xff1a 键Key 一 xff1a 概念 开源的 xff08 BSD协议 xff09 xff0c 使用ANSI C 编写 xff0c 基于
  • Hive建表以及导入数据

    目录 一 xff1a 内部表和外部表 1 xff1a 外部表 2 xff1a 外部表 3 xff1a 外部表和内部表区别 二 xff1a 上传数据方式 一 xff1a 内部表和外部表 1 xff1a 外部表 内部表基础建表语句一 默认指定文
  • Hive行列互转

    目录 一 xff1a 行转列 1 xff1a lateral view 行转列 2 xff1a explode函数 3 xff1a 例子 二 xff1a 列转行 1 概念 2 xff1a 例子 一 xff1a 行转列 1 xff1a lat
  • Hive行转列的应用之计算公司累加收入

    公司代码 年度 1月 12月的收入金额 burk year tsl01 tsl02 tsl03 tsl04 tsl05 tsl06 tsl07 tsl08 tsl09 tsl10 tsl11 tsl12 853101 2010 100200
  • Hive的常用函数

    目录 1 关系运算 2 数值计算 3 条件函数 4 日期函数 5 xff1a 字符串函数 6 xff1a Hive 中的wordCount 1 关系运算 等值比较 61 61 61 lt 61 gt 不等值比较 61 lt gt 区间比较
  • IDEA配置spring环境,并简单测试

    目录 一 下载安装需要的依赖包 二 IDEA的部署 一 下载安装需要的依赖包 首先 xff0c 我们需要下载一点jar包 第一个jar包是第三方依赖的jar包 xff0c Spring的核心容器依赖commons logging的JAR包
  • Unity导出工作台(Console)数据

    首先在Unity中添加C 脚本 xff1a using System Collections using System Collections Generic using UnityEngine using UnityEditor usin
  • Unity利用代码生成空心立方体(立方体挖走一个圆柱)

    先看效果 还未生成mesh时挖去圆柱的立方体 生成mesh后挖去圆柱的立方体 放代码为敬 xff08 脚本挂在空物体上即可 xff09 xff1a using System Collections using System Collecti
  • 企业ERP系统开发总结及建议

    企业ERP系统开发总结及建议 作者 xff1a 成晓旭 对于像我们这种规模的大型公司 xff0c 自己建设 实施和维护满足公司特定管理要求的管理信息系统 xff0c 是目前部分大型公司建设企业ERP 的常见思路 比如 xff1a XXXX
  • Unity 3D导入txt文本文件坐标并打印

    先看打印结果 xff1a 首先我们创建一个txt文件 xff0c 将坐标输入或复制进去 xff0c 从左到右依次为x y z xff0c 中间用逗号 xff08 英文逗号 xff09 隔开 在Unity工程文件下的Assets文件夹下创建R
  • 2022研究生数学建模竞赛(华为杯)B题

    题目 xff1a 方形件组批优化问题 一 背景介绍 智能制造被 中国制造 2025 列为主攻方向 而个性化定制 更短的产品及系统生命周期 互联互通的服务模式等成为目前企业在智能制造转型中的主要竞争点 以离散行业中的产品为例 xff0c 如电
  • 2022研究生数学建模B题思路

    子问题1 xff1a 排样优化问题 要求建立混合整数规划模型 xff0c 在满足生产订单需求和相关约束条件下 xff0c 尽可能减少板材用量 约束 1 在相同栈 stack xff09 里的产品项 item xff09 的宽度 xff08
  • 找到并标记Mesh顶点

    1 在Unity 3D中新建一个物体 本文以Cube为例 2 创建一个C 脚本 命名为MeshTest 3 在脚本中写入程序 在打开的脚本 MeshTest 上编写代码 xff0c 首先获取 MeshFilter 组件 xff0c 然后获取

随机推荐

  • TCP服务器端、客户端通讯(赋源码)

    实现通讯 xff0c 我们首先要知道是怎么样的一个流程 xff0c 下图是我画的一个通讯流程图 xff1a 一 Linux服务器端 我是在Ubuntu20 04下进行的 xff0c 使用的是C 43 43 xff0c 引入头文件socket
  • win11 命令 wmic:无效的指令 解决办法

    我想你肯定看到过让你修改环境变量的方法 但是 xff0c 如果你的电脑就根本没有装wmic xff0c 再怎么修改环境变量也是徒劳 我们打开设置 xff1a Win 43 I 点击应用 选择 可选功能 添加可选功能 搜索wmic xff0c
  • Python面向对象编程:关于类的方法中属性是否加前缀self的问题

    问题的缘起 今天完成了LeetCode首秀 xff08 而且是用刚学不久的python做的 xff09 xff0c 心情挺激动的 xff0c 毕竟之前只涉猎了竞赛OJ xff0c 没有在应用型平台上刷过题 xff0c 不妨定一个小目标 xf
  • 概念物理Ⅱ 第一讲:绪论

    目录 物理学研究的对象和动机为什么要研究物理学 xff1f 1 满足人类对大自然基本规律的好奇心2 改进技术 发展生产 两个动机之间的关系 物理学的发展历史起源于古希腊古典物理学时间 xff1a 牛顿时代 1900年经典力学 xff08 牛
  • Machine Learning:k近邻算法(KNN)

    目录 写在前面的话k 近邻算法概述优点缺点适用数据范围 原理Python代码实现Sklearn直接调用weights选项algorithm选项 算法测试与结果评价原理及方法函数主要参数说明Python代码实现 示例反思与总结 写在前面的话
  • 《Python爬虫技术:深入理解原理、技术与开发》读书笔记(一)

    目录 前言第1章 基础知识第2章 爬虫基础HTTP基础URL与URI超文本HTTP与HTTPSHTTP的请求过程Network面版 前言 这是本系列的第一篇文章 xff0c 文如其题 xff0c 这个系列旨在学习Python爬虫技术 本系列
  • 给软件工程师的自学建议

    给软件工程师的自学建议 与现在大学生的情况类似 xff0c 学校学的专业知识总是与实际工作中需要的知识相差甚远 或许进入我们这个行业就注定要一辈子不离书本 不离学习了 由于软硬件技术的推陈出新 xff0c 学校教的C Basic Pasca
  • Python基础入门—for循环

    Python基础入门 for循环 for 循环 xff1a range的使用 xff1a 循环控制语句 xff1a for else的使用 xff1a for循环嵌套 xff1a for 循环 xff1a for循环格式 xff1a for
  • 软件测试之项目总结全攻略

    在我们测试工作过程中 xff0c 由于公司业务发展 xff0c 快速迭代等原因 xff0c 我们遇到的项目以小项目居多 更新界面元素 xff0c 上个活动页 xff0c 优化一下原有的功能等等 xff0c 加上事情繁琐 xff0c 任务多
  • 教你用Python写一个京东自动下单抢购脚本(Python实现京东自动抢购)

    很多朋友都有网购抢购限量商品的经历 有时候蹲点抢怎么也抢不到 今天小编带你们学习怎么用Python写一个京东自动下单抢购脚本 以后再也不用拼手速拼网速啦 快来一起看看吧 1 问题背景 经过无数次抢购失败后 xff0c 发现商家会不定时的放出
  • JAVA基础题练习

    顺序插入 xff1a 插入有序 Scanner input 61 new Scanner System in char arr2 61 39 b 39 39 d 39 39 f 39 39 i 39 39 k 39 39 m 39 39 x
  • ubuntu20.04安装qq音乐并解决闪退问题

    在qq音乐官网下载linux版coco音乐 xff1a https y qq com download download html 下载deb包并且通过下面命令行安装 xff1a span class token function sudo
  • 架构师装逼核武器

    架构师这个职位是很多程序猿的梦想 xff0c 我有很多朋友私下和我聊天的时候 xff0c 曾多次问我要如何才能成为一个架构师 xff0c 对于这个问题 xff0c 我只能粗略的谈谈我个人的观点 xff0c 如有不同观点 xff0c 欢迎交流
  • 一文讲透java日志框架

    在项目开发过程中 xff0c 有一个必不可少的环节就是记录日志 xff0c 相信只要是个程序员都用过 xff0c 可是咱们自问下 xff0c 用了这么多年的日志框架 xff0c 你确定自己真弄懂了日志框架的来龙去脉嘛 xff1f 下面笔者就
  • HashMap底层原理

    在我们实际的项目中 xff0c HashMap这个集合类经常被用到 xff0c 可是就是这么一个常用的集合类 xff0c 却往往成了咱们面试中的绊脚石 即便你是个初级程序员 xff0c 也常会让你谈谈HashMap的底层原理 xff0c 今
  • 聊聊ThreadPoolExecutor线程池

    ThreadPoolExecutor是线程的池化技术 xff0c 也就是首先创建几个线程 xff0c 然后把线程放到池子里 xff0c 有任务来的时候直接从线程池中拉线程来执行任务 为什么要用池化技术 xff1f java中的线程是系统级别
  • Spring框架系列之bean的生命周期底层原理06

    bean的生命周期 xff0c 咱们必须从 AnnotationConfigApplicationContext的getBean方法开始 xff0c getBean顾名思义就是从Spring容器中得到一个Bean的实例对象 xff0c Sp
  • 电信运营商移动互联网发展分析

    电信运营商移动互联网发展分析 移动互联网是通信业发展的大趋势 xff0c 随着3G 和WiMAX 等高速无线接入技术的飞速发展 xff0c 移动互联网不仅继承固定互联网的很多技术 xff0c 并且在商务 娱乐以及移动性等方面拓展用户需求 自
  • Spring框架系列之bean的生命周期底层原理07

    上一篇我们预留了两个大的内容 xff0c 一个是Object sharedInstance 61 getSingleton beanName 从单例池中获取数据 xff0c 另外一个是getSingleton方法创建单例Bean xff0c
  • Spring框架系列之bean的生命周期底层原理08

    接着上一篇 xff0c 咱们继续doCreateBean方法的分析 xff0c doCreateBean内容比较多 xff0c 我们这次主要是把它的整体流程说下 xff0c 后续会逐个来分析每一个关键点 代码如下 xff1a protect