Spring Bean工厂揭秘:提前暴露的艺术与源码深度解析

1. 引言

在Spring框架中,Bean的生命周期是一段复杂的旅程,涉及创建、初始化和销毁等关键步骤。而其中一个鲜为人知的技巧——提前暴露Bean工厂对象,则是这段旅程中一道亮丽的风景线。它不仅解决了循环依赖的难题,还优化了应用的性能。接下来,将结合源码和实例,深入探索这一特性的奥秘。


2. 提前暴露Bean工厂对象的概念

在Spring中,当某个Bean的实例化过程还未完成时,其引用可能会被其他Bean所依赖。为了解决这种潜在的循环依赖问题,Spring采用了提前暴露Bean工厂对象的策略。这意味着,在Bean实例化完成后、但尚未完全初始化之前,其引用就会被添加到容器的缓存中,以供其他Bean引用。


3. 提前暴露Bean的时机

提前暴露Bean的时机是在Bean实例化完成后、但属性填充和初始化方法调用之前。这个时机选择非常重要,因为它确保了在Bean的创建过程中尽早地解决了循环依赖问题,同时又不影响Bean的正常初始化过程。


3. 提前暴露Bean的作用

  1. 解决循环依赖问题
    • 当两个或多个Bean之间存在相互依赖的关系时,即循环依赖,传统的依赖注入方式会导致问题。但Spring通过提前暴露Bean的引用,可以在Bean完全初始化之前解决这种依赖关系。
    • 例如,Bean A依赖于Bean B的创建,而Bean B在创建过程中又依赖于Bean A。通过提前暴露Bean A的引用给Bean B,Spring能够成功完成这种循环依赖的注入。
  2. 优化性能
    • 提前暴露Bean工厂对象可以减少不必要的等待时间。在Bean的创建过程中,其他Bean可以尽早地获取到正在创建的Bean的引用,从而减少了因等待Bean完全初始化而可能产生的延迟。
    • 这对于大型、复杂的Spring应用来说尤为重要,因为它能够显著提高应用的启动速度和响应性能。
  3. 简化应用配置和管理
    • 通过将复杂的Bean创建过程封装在FactoryBean中,并提前暴露FactoryBean的引用,其他Bean可以直接使用该FactoryBean来获取所需的Bean实例。
    • 这使得应用配置更加简洁和清晰,同时也方便了对Bean的管理和维护。
  4. 支持特定框架和库
    • FactoryBean还可以用于创建和管理第三方库中的对象,如Hibernate的SessionFactory、MyBatis的SqlSessionFactory等。
    • 这些对象通常需要复杂的配置和初始化过程,通过FactoryBean可以简化这些操作,使得集成第三方库变得更加容易。
  5. 扩展Spring功能
    • FactoryBean提供了一种扩展Spring功能的方式。通过自定义FactoryBean,开发者可以实现一些框架级别的功能,如AOP、事务管理等。
    • 这使得Spring框架更加灵活和强大,能够满足各种复杂的业务需求。

总结来说,提前暴露Bean工厂对象在Spring框架中扮演着至关重要的角色。它不仅能够解决循环依赖问题、优化性能、简化应用配置和管理,还能够支持特定框架和库、扩展Spring功能。这些作用使得Spring框架在实际应用中更加灵活、高效和强大。


4. 底层源码分析

在Spring的底层实现中,提前暴露Bean的引用主要通过DefaultSingletonBeanRegistry类中的getSingleton方法完成。以下是该方法的关键部分:

  • 当容器尝试获取某个Bean的引用时,会调用getSingleton方法。
  • getSingleton方法首先检查该Bean是否已经在单例缓存中存在(即是否已经完全初始化)。
  • 如果不存在,但正在创建中(即存在未完成的创建任务),则Spring会将其ObjectFactory(用于创建Bean的工厂对象)添加到缓存中,并返回该ObjectFactory的代理对象。
  • 其他Bean在尝试引用该Bean时,会通过该代理对象获取到正在创建的Bean的引用。

这样,即使Bean尚未完全初始化,其他Bean也能通过代理对象获取到其引用,从而解决了循环依赖问题。


5. 实例

  1. Bean的实例化与提前暴露
    • 假设我们有两个相互依赖的Bean:DatabaseConfigDataSourceDatabaseConfig 需要注入 DataSource 的引用,而 DataSource 又需要依赖 DatabaseConfig 中的某些配置信息。
@Component  
public class DatabaseConfig {  // ... 省略其他代码 ...  @Bean  public DataSource dataSource() {  // 使用DatabaseConfig中的配置信息来创建DataSource  // 假设这里会用到DatabaseConfig的其他属性或方法  return new DataSource(/* 依赖DatabaseConfig的配置 */);  }  
}  @Component  
public class SomeService {  @Autowired  private DatabaseConfig databaseConfig; // 依赖DatabaseConfig  // ... 省略其他代码 ...  
}

在Spring的容器中,当开始创建 DatabaseConfig 的Bean时,一旦 DatabaseConfig 的实例被创建出来(但还未完成初始化),Spring就会将其引用提前暴露到容器中。这样,当 SomeService 需要注入 DatabaseConfig 的引用时,即使 DatabaseConfigdataSource() 方法还未被调用,Spring也能提供其引用。

  1. 解决循环依赖的实例
    • 继续上面的例子,如果 DataSource 也需要注入 DatabaseConfig 的引用(这在现实中并不常见,但为了演示循环依赖,我们假设存在这样的场景):
@Component  
public class DataSource {  private DatabaseConfig databaseConfig;  @Autowired  public DataSource(DatabaseConfig databaseConfig) {  this.databaseConfig = databaseConfig;  // ... 使用databaseConfig进行初始化 ...  }  
}

由于 DatabaseConfigDataSource 之间存在循环依赖,传统的依赖注入方式会陷入死循环。但Spring通过提前暴露 DatabaseConfig 的Bean引用,使得 DataSource 能够在 DatabaseConfig 完全初始化之前注入其引用。这样,循环依赖问题就得到了解决。


6. 总结

提前暴露Bean工厂对象是Spring框架中一个重要的技巧,它巧妙地解决了循环依赖问题,并优化了应用的性能。通过深入解析源码和实例,可以更好地理解这一特性的原理和应用场景。在实际开发中,应该充分利用Spring框架提供的这一特性,来构建更加健壮、高效的应用。


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

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

相关文章

前端优化的时间库

将moment.js换成day.js day.js的体积比moment.js小。moment.js有70多kb,但是day.js只有2kb。像微信小程序这种对代码包大小有要求的情况下,day.js会是比moment.js更好的选择。很多官方的框架和库都已经将moment.js换成了day.js。 moment已经好几年没…

【强化学习】Q-learning,DQN,SARSA算法介绍

【强化学习】Q-learning,DQN,SARSA算法介绍 强化学习算法分类基于价值的方法基于策略的方法Actor-Critic方法 Q-learning算法DQN算法强化学习训练数据存在的问题经验回放机制备份网络机制 Sarsa算法总结 强化学习算法分类 按学习目标进行分类 可分为基于…

【数理统计03】集中不等式

集中不等式(concentration inequalities)是在概率论和统计学中用于描述随机变量(尤其是随机变量的和或函数)的集中程度的一类不等式。它们为随机变量偏离其期望值的概率提供了上界。这些不等式在很多领域都有应用,包括…

使用OpenSSL生成证书和私钥文件

使用OpenSSL生成证书和私钥文件,请按以下步骤操作: 第1步:生成RSA私钥 openssl genrsa -out private_key.pem 2048参数 -out 指定生成的私钥文件名,参数 2048 指定生成的RSA私钥以位为单位的长度,常见的取值&#xf…

Oracle表空间加密全过程极简实验

加密前 准备加密HR Schema中的表。 SQL> col table_name for a20 SQL> select table_name, tablespace_name from all_tables where ownerHR;TABLE_NAME TABLESPACE_NAME -------------------- ------------------------------ DEPARTMENTS …

json文件写操作

一、背景 二、json写操作 2.1 json文件写操作,拷贝初版 //json文件写操作,拷贝初版void json_write() {//1. 定义对象 { }cJSON* interest cJSON_CreateObject();// 插入元素,对应 键值对cJSON_AddItemToObject(interest, "combat", cJSON_CreateStri…

【基于Fluent和深度学习算法驱动的流体力学计算与应用】

在深度学习与流体力学融合的背景下,科研边界不断拓展,创新成果层出不穷。从物理模型融合到复杂流动模拟,从数据驱动研究到流场智能分析,深度学习正以前所未有的力量重塑流体力学领域。目前在Nature和Science杂志上发表的深度学习驱…

Leetcode 力扣93. 复原IP地址 (抖音号:708231408)

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 . 分隔。 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.2…

常见乱码分析

java编解码 如果该编码本身支持中文,同类型 编解码,中文不会乱码, 如果该编码本身不支持中文(如:US_ASCII,ISO_8859_1),同类型 编解码,中文也会乱码 StandardCharsets 不…

uinapp 写好的聊天基础页面,拿来就用非常方便

<template><view class"chat"><!-- <u-navbar :title"name" :placeholdertrue leftClick"goback"></u-navbar> --><!-- 顶部标题 --><view class"topTabbar"><!-- 返回图标 --><u…

Linux 中的进程优先级管理

在 Linux 系统中&#xff0c;理解和管理进程优先级是维护系统性能的关键因素。本文将详细介绍进程优先级&#xff08;priority&#xff09;的基本概念、如何查看和调整进程优先级&#xff0c;以及 nice 值对优先级的影响。 基本概念 在多任务操作系统中&#xff0c;CPU 资源的…

Power BI 使用Filter()函数完成类似子查询的筛选

1. 假如我们有两张表&#xff0c;如下图&#xff0c;以及它们的关联方式&#xff1a; tb_bursary.student_id tb_student.id 2. 我们想要实现这个逻辑&#xff0c;先找出tb_student里&#xff0c;sno最大的学生id&#xff0c;再根据查找出的学生id&#xff0c;找到tb_bursary…

TS 进阶类型

联合类型 | 当 TS 不确定一个联合类型的变量到底是哪个类型的时候,可以定义多种类型&#xff0c;例如&#xff0c;一个变量既支持 number 类型&#xff0c;又支持 string 类型. let num: 类型1 | 类型2 | 类型3 .... 初始值 let num:number | string 1 // 可以写多个类型 /…

C++笔试强训day34

目录 1.ISBN号码 2.kotori和迷宫 3.矩阵最长递增路径 1.ISBN号码 链接https://www.nowcoder.com/practice/95712f695f27434b9703394c98b78ee5?tpId290&tqId39864&ru/exam/oj 提取题意&#xff0c;模拟一下即可。 #include <iostream> using namespace std; …

学习笔记:C语言的32个关键字

一、标准C语言的32个关键字 1、基本数据类型&#xff1a; signed unsigned char int float double short long void 2、构造数据类型&#xff1a; struct union enum 3、数据存储类别&#xff1a; auto static extern register 4、数据优化&#xff1a; const volatile 5、9条…

Java(其十二)--集合·初级

ArrayList集合 集合有很多种&#xff0c;ArrayList 是最常用的一种&#xff0c;集合的作用相当于C中的STL 最显著的特点就是&#xff1a;自动扩容。 一般定义式 ArrayList list new ArrayList(); //该 list 是可以储存各种类型的数据的&#xff0c;要想约束储存的数据&#x…

买车是小米su7还是model3?这个AI在我这里“干掉了”百万车评人

作者 | 曾响铃 文 | 响铃说 43天交付1万辆新车&#xff01;雷军的微博一发&#xff0c;又把小米汽车推上了热搜。 自小米su7问世以来&#xff0c;天天刷屏。说不心动&#xff0c;那是假的&#xff0c;身边好几个朋友都按捺不住要下订一台了。 但真要买&#xff0c;还是忍不住…

[回溯法]子集和数问题

没有任何优化&#xff0c;纯深搜做法&#xff1a; #include<iostream> #include<vector> using namespace std;const int N 100010; int nums[N],selected[N]; int n,M,sum; bool ansfalse;void Out() {for (int i 0; i < n - 1; i)printf("%d", …

Gateway+Oauth2授权码登录

在Oauth服务中配置 @Override protected void configure(HttpSecurity http) throws Exception {http.csrf().disable().formLogin().permitAll().and()//主要配置好.formLogin().authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll().antMatche…

强大的医院绩效考核管理系统源码,支持行业内所有绩效方案,且每步核算都可自主进行方案的新建、调整。

医院绩效考核管理系统是采用B/S架构模式设计、使用JAVA语言开发、后台使用MySql数据库进行管理的一整套计算机应用软件源码。 系统和his系统进行对接&#xff0c;按照设定周期&#xff0c;从his系统获取医院科室和医生、护士、其他人员工作量&#xff0c;对没有录入信息化系统…