Spring IOC - Bean的生命周期之实例化

        在Spring启动流程文章中讲到,容器的初始化是从refresh方法开始的,其在初始化的过程中会调用finishBeanFactoryInitialization方法。

        而在该方法中则会调用DefaultListableBeanFactory#preInstantiateSingletons方法,该方法的核心作用是初始化非延迟加载的Bean,且提供了两个扩展点。源码及注释如下:

@Override
public void preInstantiateSingletons() throws BeansException {

   //该方法先复制一份BeanDefinition名称列表,为了防止在初始化过程中有新的BeanDefinition被注册,
   //从而导致遍历过程中的并发修改异常
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   //对于每个非抽象、单例且非懒加载的 BeanDefinition,如果它是一个 FactoryBean,
   //则获取 FactoryBean 的实例,如果 FactoryBean 实现了 `SmartFactoryBean` 接口,
   //则调用 `isEagerInit` 方法判断是否需要预先实例化,默认为需要预先实例化。
   //如果该 Bean 需要预先实例化,则调用 `getBean` 方法进行实例化。
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      // 不是抽象类&&是单例&&不是懒加载
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               FactoryBean<?> factory = (FactoryBean<?>) bean;
               boolean isEagerInit;
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                  isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
               }
               else {
                  isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
               }
               if (isEagerInit) {
                  getBean(beanName);
               }
            }
         }
         else {
            // 这里就是普通单例Bean正式初始化了
            getBean(beanName);
         }
      }
   }

   //对于每个实现了SmartInitializingSingleton接口的单例Bean,调用其afterSingletonsInstantiated方法
   // 执行时机是在bean的生命周期最后,即在bean完成实例化、属性注入、相关初始化操作后
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
               smartSingleton.afterSingletonsInstantiated();
               return null;
            }, getAccessControlContext());
         }
         else {
            // 比如:ScheduledAnnotationBeanPostProcessor CacheAspectSupport  MBeanExporter等等
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}

        对该方法可简单总结为以下三点:

  1. 对于实现了SmartFactoryBean的子类,如果isEagerInit(立即初始化)返回true,则对本是懒加载的getObject对象立即初始化;

  2. 否则,正常调用getBean方法开始bean的生命周期;

  3. 在bean的生命周期处理结束后,对实现了SmartInitializingSingleton接口的单例Bean,调用其afterSingletonsInstantiated方法

          继续就是"干实事"的方法:AbstractBeanFactory#doGetBean,其逻辑流程图如下所示:

          其中创建Bean的核心方法为AbstractAutowireCapableBeanFactory#createBean,源码及注释如下:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   RootBeanDefinition mbdToUse = mbd;
   
   //确保实际解析了bean Class类型
   //并在动态解析类的情况下克隆beanDefinition,因为动态解析的类无法存储在共享的合并beanDefinition中
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overrides.
   try {
   // 预先标记没有重载的方法,以避免参数类型检查的开销
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // 实例化前阶段:让BeanPostProcessors有机会返回代理而不是目标bean实例
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      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);
   }
}

        在真正实例化之前,如果bean实现了InstantiationAwareBeanPostProcessor接口,则会调用其postProcessBeforeInstantiation方法,这就是bean生命周期的实例化前阶段。该接口提供的核心方法及作用如下表所示:

方法名称

作用阶段

作用

postProcessBeforeInstantiation

实例化前

该方法传入目标Bean类型与BeanName;该方法可以返回一个该Bean类型的对象,或对该Bean的一个代理对象; 当该方法返回了实例化对象后,后续的所有Bean实例化与初始化的动作将不再进行。只会调用后续的BeanPostProcessor#postProcessAfterInnitialization方法

postProcessAfterInstantiation

实例化后

该方法传入还没有装配属性的Bean对象以及BeanName 如果该方法返回false,则将跳过后续的属性装配动作,一般应该返回true

postProcessProperties

实例化后

属性填充前

该方法传入在配置期间所配的PropertyValues以及BeanName 该方法返回的PropertyValues将最终装配到Bean对象中

        接下来是doCreateBean方法,其逻辑流程图如下:

  1. 先检查instanceWrapper变量是不是null,这里一般是null,除非当前正在创建的Bean在factoryBeanInstanceCache中存在这个是保存还没创建完成的FactoryBean的集合。

  2. 调用createBeanInstance方法实例化Bean,这个方法在后面会讲解

  3. 如果当前RootBeanDefinition对象还没有调用过实现了的MergedBeanDefinitionPostProcessor接口的方法,则会进行调用

  4. 当满足以下三点 (1)是单例Bean (2)尝试解析bean之间的循环引用 (3)bean目前正在创建中 则会进一步检查是否实现了SmartInstantiationAwareBeanPostProcessor接口如果实现了则调用是实现的getEarlyBeanReference方法

  5. 调用populateBean方法进行属性填充

  6. 调用initializeBean方法对Bean进行初始化

        关键方法AbstractAutowireCapableBeanFactory#createBeanInstance源码及注释如下:

// 使用适当的实例化策略为指定的bean创建一个新实例:工厂方法、构造函数自动装配或简单实例化
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   // 获取bean的Class对象
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   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());
   }

   // 通过bd中提供的instanceSupplier来获取一个对象
   // 正常bd中都不会有这个instanceSupplier属性,这里也是Spring提供的一个扩展点,但实际上不常用
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   // 如果工厂方法不为null,则使用工厂方法初始化策略
   // bd中提供了factoryMethodsName属性,那么要使用工厂方法的方法来创建对象
   // 工厂方法又会区分静态工厂方法跟实例工厂方法
   if (mbd.getFactoryMethodName() != null) {
      // 如果使用了工厂方法,则调用工厂方法创建bean实例。@Bean注解创建的实例会进入这里
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same bean...
   // 在原型模式下,如果已经创建过一次这个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);
}

        总结如下:

  1. 先检查Class是否已经关联了,并且对应的修饰符是否是public的

  2. 如果用户定义了Bean实例化的函数,则调用并返回

  3. 如果当前Bean实现了FactoryBean接口则调用对应的FactoryBean接口的getObject方法

  4. 根据getBean时候是否传入构造参数进行处理

  • 4.1 如果没有传入构造参数,则检查是否存在已经缓存的无参构造器,有则使用构造器直接创建,没有就会调用instantiateBean方法先获取实例化的策略默认是CglibSubclassingInstantiationStrategy,然后实例化Bean。最后返回

  • 4.2 如果传入了构造参数,则会先检查是否实现了SmartInstantiationAwareBeanPostProcessor接口,如果实现了会调用determineCandidateConstructors获取返回的候选构造器。

  • 4.3 检查4个条件是否满足一个 (1)构造器不为null, (2)从RootBeanDefinition中获取到的关联的注入方式是构造器注入(没有构造参数就是setter注入,有则是构造器注入) (3)含有构造参数 (4)getBean方法传入构造参数不是空 满足其中一个则会调用返回的候选构造器实例化Bean并返回,如果都不满足,则会根据构造参数选则合适的有参构造器然后实例化Bean并返回

  • 如果上面都没有合适的构造器,则直接使用无参构造器创建并返回Bean。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CCF CSP认证历年题目自练Day45

这几天搞泰迪杯数据分析技能赛去了。等拿国奖了就出一期关于泰迪杯的。 题目 试题编号&#xff1a; 201703-3 试题名称&#xff1a; Markdown 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   Markdown 是一种很流行的轻量级标记…

Redis键(Keys)

前言 在 Redis 中&#xff0c;键&#xff08;Keys&#xff09;是非常重要的概念&#xff0c;它们代表了存储在数据库中的数据的标识符。对键的有效管理和操作是使用 Redis 数据库的关键一环&#xff0c;它直接影响到数据的存取效率、系统的稳定性和开发的便利性。 本文将深入…

Android开发中自定义圆盘中的加速器的表盘

话不多说先上最终的效果图&#xff1a; 本篇文章主要介绍怎么自定义一个带刻度的圆盘&#xff0c;主要包含绘制内外圆环以及刻度的绘制。具体的实现如下&#xff1a; package com.dz.common.view;import android.animation.ValueAnimator; import android.content.Context; im…

在报错中学python something

这里写目录标题 动手学深度学习pandas完整代码数据处理TypeError: can only concatenate str (not "int") to str&#xff08;fillna填补缺失值&#xff09; 创建文件夹学习这个数据分组get_dummies实现one hot encode 动手学深度学习pandas完整代码 import osimpor…

Camtasia2024全新中文版电脑录屏工具

在这个视频的大舞台上&#xff0c;每一帧都是你炫耀的机会&#xff0c;每一秒都是让观众瞪大眼睛的瞬间。现在&#xff0c;让我们一起飞跃时空&#xff0c;用更少的时间创作更多的惊喜吧&#xff01; 就算你是个小白&#xff0c;毫无经验&#xff0c;别担心&#xff0c;Camtas…

记录C# WinForm项目调用Rust生成的dll库

一、开发环境 1.RustRover (version&#xff1a;2023.3 EAP) 2.Visual Studio 2019 (version&#xff1a;16.11.30) 3.Windows 10 64位 OS 4.WinR&#xff1a;控制台程序&#xff0c;cmd.exe 二、使用RustRover编译Rust脚本为dll 1.下载安装Rust&#xff0c;https://www.…

Vue3封装自定义指令+h()

官方install介绍 directive/myDir/index.js 定义指令 import { h, render, ref } from "vue"; const vMyDir {mounted(el, binding) {renderElement(el, binding);}, }; // inserted是Vue2的生命周期钩子&#xff0c;所以在Vue3项目中要使用mounted const renderEl…

Android Rxjava架构原理与使用的详解解答

简单介绍 Rxjava这个名字&#xff0c;其中java代表java语言&#xff0c;而Rx是什么意思呢&#xff1f;Rx是Reactive Extensions的简写&#xff0c;翻译过来就是&#xff0c;响应式拓展。所以Rxjava的名字的含义就是&#xff0c;对java语言的拓展&#xff0c;让其可以实现对数据…

从头开始的卷积神经网络

VGG-16 卷积神经网络。来源&#xff1a;LearnOpenCV 参考资料&#xff1a;这篇文章可以在 Kaggle Notebook &#x1f9e0; Convolutional Neural Network From Scratch上更好地阅读。路易斯费尔南多托雷斯 一、说明 本文详细介绍在tf2.0上&#xff0c;使用ceras实现基本的神经…

「NLP+网安」相关顶级会议期刊 投稿注意事项+会议等级+DDL+提交格式

「NLP网安」相关顶级会议&期刊投稿注意事项 写在最前面一、会议ACL (The Annual Meeting of the Association for Computational Linguistics)IH&MMSec (The ACM Workshop on Information Hiding, Multimedia and Security)CCS (The ACM Conference on Computer and Co…

16 _ 二分查找(下):如何快速定位IP对应的省份地址?

通过IP地址来查找IP归属地的功能,不知道你有没有用过?没用过也没关系,你现在可以打开百度,在搜索框里随便输一个IP地址,就会看到它的归属地。 这个功能并不复杂,它是通过维护一个很大的IP地址库来实现的。地址库中包括IP地址范围和归属地的对应关系。 当我们想要查询202…

Golang源码分析 | 程序引导过程

环境说明 CentOS Linux release 7.2 (Final&#xff09; go version go1.16.3 linux/amd64 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7使用gdb查看程序入口 编写一个简单的go程序 // main.go package mainfunc main() {print("Hello world") } 编译go …

Python大神用的贼溜的九个技巧,超级实用~

文章目录 一、整理字符串输入二、迭代器&#xff08;切片&#xff09;三、跳过可对对象的开头四、只包含关键字参数的函数 (kwargs)五、创建支持「with」语句的对象六、用「slots」节省内存七、限制「CPU」和内存使用量八、控制可以/不可以导入什么九、实现比较运算符的简单方法…

js获取当前日期与7天后的日期

调用 console.log(this.getSectionData(7))结果 函数 getSectionData(section) {const now new Date()const nowYear now.getFullYear()const nowMonth now.getMonth() 1 < 10 ? (0 (now.getMonth() 1)) : (now.getMonth() 1)const nowDay now.getDate() < 1…

Git 分支设计规范

开篇 这篇文章分享 Git 分支设计规范&#xff0c;目的是提供给研发人员做参考。 规范是死的&#xff0c;人是活的&#xff0c;希望自己定的规范&#xff0c;不要被打脸。 在说 Git 分支规范之前&#xff0c;先说下在系统开发过程中常用的环境。 DEV 环境&#xff1a;用于开发…

高可用架构设计

1. 引言 软件系统有三个追求&#xff1a;高性能、高并发、高可用&#xff0c;俗称三高。三者既有区别也有联系&#xff0c;门门道道很多&#xff0c;本篇讨论高可用 高可用技术的重要性在于保证系统的连续可用性&#xff0c;提高系统的稳定性和可靠性。它可以应对高并发和大规…

vue2按需导入Element(vite打包)

1.安装element 说明&#xff1a;-S是生产依赖。 npm install element-ui2 -S 2.安装babel-plugin-component 说明&#xff1a;-D是开发模式使用。 npm install babel-plugin-component -D 3. vite.config.js 说明&#xff1a;借助 babel-plugin-component &#xff0c;我们可…

华为的干部管理和人才管理实践精髓(深度好文,收藏)

&#xff08;本文摘自谢宁专著《华为战略管理法&#xff1a;DSTE实战体系》&#xff0c;欢迎购买&#xff09; 1997年&#xff0c;在《华为基本法》的起草过程中&#xff0c;起草小组的一位人大教授问任正非:“任总&#xff0c;人才是不是华为的核心竞争力?”任正非的回答出人…

在Spring Boot中使用进程内缓存和Cache注解

在Spring Boot中使用内缓存的时候需要预先知道什么是内缓存&#xff0c;使用内缓存的好处。 什么是内缓存 内缓存&#xff08;也称为进程内缓存或本地缓存&#xff09;是指将数据存储在应用程序的内存中&#xff0c;以便在需要时快速访问和检索数据&#xff0c;而无需每次都从…

记录--让我们来深入了解一下前端“三清”是什么

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前端“三清” 在前端开发中&#xff0c;我们经常听到关于“三清”的说法&#xff0c;即 window、document、Object。这三者分别代表了 BOM(浏览器对象模型)、DOM(文档对象模型)以及 JS 的顶层对象。在…