当前位置: 首页 > news >正文

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。

相关文章:

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

在Spring启动流程文章中讲到&#xff0c;容器的初始化是从refresh方法开始的&#xff0c;其在初始化的过程中会调用finishBeanFactoryInitialization方法。 而在该方法中则会调用DefaultListableBeanFactory#preInstantiateSingletons方法&#xff0c;该方法的核心作用是初始化…...

前端 BUG 总结

文章目录 CSS 样式1、Chrome 89 版本期不再支持 /deep/&#xff0c;请勿使用嵌套 /deep/2、圆角按钮 button 点击后出现矩形框线3、怪异模式4、border 1 像素在手机上显示问题5、文本溢出问题 JavaScript 脚本1、移动端点击穿透2、使用parseInt时必须补全第二个参数 radix3、有…...

【蓝桥杯软件赛 零基础备赛20周】第3周——填空题

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 文章目录 00. 2023年第14届参赛数据0. 上一周答疑1. 填空…...

Pytorch自动混合精度的计算:torch.cuda.amp.autocast

1 autocast介绍 1.1 什么是AMP? 默认情况下&#xff0c;大多数深度学习框架都采用32位浮点算法进行训练。2017年&#xff0c;NVIDIA研究了一种用于混合精度训练的方法&#xff0c;该方法在训练网络时将单精度&#xff08;FP32&#xff09;与半精度(FP16)结合在一起&#xff…...

一文看懂香港优才计划和高才通计划的区别和优势?如何选?

一文看懂香港优才计划和高才通计划的区别和优势&#xff1f;如何选&#xff1f; 为什么很多人都渴望有个香港身份&#xff1f; 英文这里和内地文化相近&#xff0c;语言相通&#xff0c;同时税率较低、没有外汇管制&#xff0c;有稳定金融体制和良好的营商环境&#xff0c;诸多…...

DTC Network旗下代币DSTC大蒜头即将上线,市场热度飙升

全球数字资产领导者DTC Network宣布其代币DSTC&#xff08;大蒜头&#xff09;即将于近期上线&#xff0c;引发市场广泛关注。DTC Network以其创新性的区块链技术和多维度的网络构建&#xff0c;致力于打造一个融合Web3.0、元宇宙和DAPP应用的去中心化聚合公共平台&#xff0c;…...

高通SDX12:ASoC 音频框架浅析

一、简介 ASoC–ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。 本文基于高通SDX12平台,对ASoC框架做一个分析。 二、整体框架 1. 硬件层面 嵌入式Linux设备的Audio subsystem可以划分为Machine(板…...

国际化:i18n

什么是国际化&#xff1f; 国际化也称作i18n&#xff0c;其来源是英文单词 internationalization的首末字符和n&#xff0c;18为中间的字符数。由于软件发行可能面向多个国家&#xff0c;对于不同国家的用户&#xff0c;软件显示不同语言的过程就是国际化。通常来讲&#xff0…...

【机器学习5】无监督学习聚类

相比于监督学习&#xff0c; 非监督学习的输入数据没有标签信息&#xff0c; 需要通过算法模型来挖掘数据内在的结构和模式。 非监督学习主要包含两大类学习方法&#xff1a; 数据聚类和特征变量关联。 1 K均值聚类及优化及改进模型 1.1 K-means 聚类是在事先并不知道任何样…...

风景照片不够清晰锐利,四招帮你轻松解决

我们大家在拍摄风景照的时候都希望能够拍摄出清晰锐利的照片。可能会有人问&#xff1a;“什么是锐利&#xff1f;”我们可以从锐度来给大家简单解说下。锐度是反映图片平面清晰度和图像边缘对比度的一个参数。锐度较高的画面&#xff0c;微小的细节部分也会表现得很清晰&#…...

List中的迭代器实现【C++】

List中的迭代器实现【C】 一. list的结构二. 迭代器的区别三. 迭代器的实现i. 类的设计ii. 重载iii. !重载iiii. begin()iiiii. end()iiiii. operator* 四.测试五. const迭代器的实现i. 实现ii 优化实现 六. 整体代码 一. list的结构 其实按照习惯来说&#xff0c;应该要专门出…...

VB.NET三层之用户查询窗体

目录 前言: 过程: UI层代码展示: BLL层代码展示: DAL层代码展示: 查询用户效果图:​ 总结: 前言: 想要对用户进行查询&#xff0c;需要用到控件DataGrideView&#xff0c;通过代码的形式将数据库表中的数据显示在DataGrideview控件中&#xff0c;不用对DatGridView控件…...

Django之路由层

文章目录 路由匹配语法路由配置注意事项转换器注册自定义转化器 无名分组和有名分组无名分组有名分组 反向解析简介普通反向解析无名分组、有名分组之反向解析 路由分发简介为什么要用路由分发&#xff1f;路由分发实现 伪静态的概念名称空间虚拟环境什么是虚拟环境&#xff1f…...

【06】VirtualService高级流量功能

5.3 weight 部署demoapp v10和v11版本 --- apiVersion: apps/v1 kind: Deployment metadata:labels:app: demoappv10version: v1.0name: demoappv10 spec:progressDeadlineSeconds: 600replicas: 3selector:matchLabels:app: demoappversion: v1.0template:metadata:labels:app…...

322. 零钱兑换

给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的。 示…...

【大模型-第一篇】在阿里云上部署ChatGLM3

前言 好久没写博客了&#xff0c;最近大模型盛行&#xff0c;尤其是ChatGLM3上线&#xff0c;所以想部署试验一下。 本篇只是第一篇&#xff0c;仅仅只是部署而已&#xff0c;没有FINETUNE、没有Langchain更没有外挂知识库&#xff0c;所以从申请资源——>开通虚机——>…...

2023-11-14 mysql-主从复制-相关文档

摘要: 2023-11-14 mysql-主从复制-相关文档 官方文档: MySQL :: MySQL 8.0 Reference Manual :: 17 Replication MySQL :: MySQL 8.0 Reference Manual :: 18 Group Replication 相关参数: mysql> show variables like %repl%; +-----------------------------------------…...

ios 对话框 弹框,输入对话框 普通对话框

1 普通对话框 UIAlertController* alert [UIAlertController alertControllerWithTitle:"a" message:"alert12222fdsfs" pr…...

(论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking

文献阅读笔记&#xff08;分层卷积特征&#xff09; 简介 题目 Hierarchical Convolutional Features for Visual Tracking 作者 Chao Ma, Jia-Bin Huang, Xiaokang Yang and Ming-Hsuan Yang 原文链接 arxiv.org/pdf/1707.03816.pdf 关键词 Hierarchical convolution…...

基于IGT-DSER智能网关实现GE的PAC/PLC与罗克韦尔(AB)的PLC之间通讯

工业自动化领域的IGT-DSER智能网关模块支持GE、西门子、三菱、欧姆龙、AB等各种品牌的PLC之间通讯(相关资料下载)&#xff0c;同时也支持PLC与Modbus协议的工业机器人、智能仪表等设备通讯。网关有多个网口、串口&#xff0c;也可选择WIFI无线通讯。无需编程开发&#xff0c;只…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...