Bean创建过程中调的方法

news2024/12/24 5:46:58

在AbstractBeanFactory中
getSingleton方法中,
调beforeSingletonCreation方法,
将beanName添加到defaultSingletonBeanRegistry的成员变量singletonCurrentlyInCreation集合中。
然后调singletonFactory.getObject方法,调到匿名beanFactory实例,

AbstractBeanFactory
getBean
doGetBean
{匿名beanFactory实例:
lambda.doGetBean {
AbstractAutowireCapableBeanFactory.createBean
}
}

创建过程:【

AbstractAutowireCapableBeanFactory.doCreateBean

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		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));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
 createBeanInstance
 		instantiateBean
 				SimpleInstantiationStrategy.instantiate
 				BeanUtils.instantiateClass

DefaultSingletonBeanRegistry.addSingleton方法
将已创建的Bean加入到容器。

三级缓存中的创建的Bean的移动

/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

在不存在循环依赖的时候, Bean创建过程的最后一步从singletonFactories中移除,添加到singletonObjects

	/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

存在循环依赖的时候,先放在三级缓存singletonFactories,
当被其他Bean调用的之后从singletonFactories移除,增加到二级缓存earlySingletonObjects,
最后从earlySingletonObjects移除,增加到三级缓存singletonObjects。

解决循环依赖的过程,主要用到了singletonFactories,不要earlySingletonObjects行不行呢?

在Spring解决循环依赖的过程中,singletonFactoriesearlySingletonObjects都起着重要作用,通常不能完全不用earlySingletonObjects,以下是具体分析:

singletonFactories的作用

  • 存储对象工厂singletonFactories是一个ObjectFactory类型的三级缓存,用于存储创建单例Bean的工厂对象。当一个Bean正在创建过程中,还未完全初始化完成时,会将其对应的对象工厂放入该缓存中。
  • 生成早期引用:通过调用对象工厂的getObject方法,可以获取到Bean的早期引用。这个早期引用可以在其他Bean依赖该Bean时进行注入,从而解决循环依赖问题。

earlySingletonObjects的作用

  • 存储早期Bean实例earlySingletonObjects也是一个用于解决循环依赖的缓存,它存储的是已经创建但还未完全初始化的Bean实例的早期引用。当从singletonFactories中获取到对象工厂并调用getObject方法生成早期引用后,会将这个早期引用放入earlySingletonObjects缓存中。
  • 避免重复创建:在后续的依赖注入过程中,如果再次需要获取该Bean的早期引用,会直接从earlySingletonObjects缓存中获取,而不需要再次调用对象工厂的getObject方法,提高了性能并避免了可能的重复创建问题。

不能完全不用earlySingletonObjects的原因

  • 性能优化:如果没有earlySingletonObjects缓存,每次获取Bean的早期引用都需要从singletonFactories中获取对象工厂并调用getObject方法,这会带来一定的性能开销。尤其是在存在复杂的循环依赖关系时,频繁的工厂调用会降低系统的运行效率。
  • 保证一致性:在多线程环境下,earlySingletonObjects可以保证在同一时间内,对于同一个正在创建的Bean,所有线程获取到的早期引用是一致的。如果没有这个缓存,可能会导致不同线程获取到不同的早期引用,从而引发数据不一致等问题。

虽然singletonFactories在解决循环依赖中是关键,但earlySingletonObjects也不可或缺,它们共同协作来高效、正确地处理循环依赖问题。

构造方法相互依赖是三级缓存机制都解决不了的循环依赖

class BeanA {
    private BeanB b;

    public BeanA(BeanB b) {
        this.b = b;
    }
}

class BeanB {
    private BeanA a;

    public BeanB(BeanA a) {
        this.a = a;
    }
}

你所写的这种直接在构造函数中相互依赖注入的方式在Spring中是不行的,会导致循环依赖问题无法正常解决,原因如下:

Spring解决循环依赖的机制限制

  • Spring在处理循环依赖时,主要依靠三级缓存机制。当创建一个Bean时,会先将其早期引用放入缓存中,以便在其他Bean依赖它时可以获取到这个早期引用进行注入。但这种机制在构造函数相互依赖的情况下存在问题,因为在构造函数执行时,Bean还未完全创建完成,此时如果直接将未完成创建的Bean作为参数传递给另一个Bean的构造函数,就会导致依赖注入失败。

解决方案

使用@Lazy注解

可以在其中一个Bean的依赖注入处使用@Lazy注解,延迟该Bean的初始化,直到真正需要使用时才进行创建。例如:

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
public class BeanA {
    private final BeanB b;

    public BeanA(@Lazy BeanB b) {
        this.b = b;
    }
}

@Component
public class BeanB {
    private final BeanA a;

    public BeanB(BeanA a) {
        this.a = a;
    }
}

在上述代码中,BeanA的构造函数中注入BeanB时使用了@Lazy注解,这样BeanBBeanA构造函数被调用时不会立即创建,而是在第一次使用BeanB时才进行创建,从而避免了构造函数循环依赖的问题。

使用属性注入或方法注入

将依赖注入方式从构造函数注入改为属性注入或方法注入,这样可以让Spring在处理循环依赖时有更多的灵活性。例如:

import org.springframework.stereotype.Component;

@Component
public class BeanA {
    private BeanB b;

    public void setB(BeanB b) {
        this.b = b;
    }
}

@Component
public class BeanB {
    private BeanA a;

    public void setA(BeanA a) {
        this.a = a;
    }
}

然后在配置类中通过@Bean注解的方法来进行依赖注入的配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public BeanA beanA() {
        return new BeanA();
    }

    @Bean
    public BeanB beanB() {
        return new BeanB();
    }

    @Bean
    public void configure(BeanA a, BeanB b) {
        a.setB(b);
        b.setA(a);
    }
}

在上述代码中,BeanABeanB使用了属性注入的方式,通过AppConfig配置类中的configure方法来手动完成依赖注入,避免了在构造函数中直接相互依赖的问题。

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

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

相关文章

三种电子画册制作方法

今天教大家三种电子画册的制作方法&#xff0c;很容易上手&#xff0c;需要的赶紧收藏起来 一、 利用在线平台--FLBOOK 1.注册并登录在线平台。 2.选择喜欢的模板&#xff0c;根据需求进行修改 3.批量上传PDF文件一键转换H5翻页电子画册 4.添加图片、文字等元素&#xff0c…

以太坊账户详解

文章目录 一、账户基本概念1.1 外部账户1.2 合约账户1.3 差异对比 二、帐户创建2.1 外部账户创建2.2 合约账户创建 三、账户数据结构3.1 账户状态3.2 账户状态结构 对比比特币的 “UTXO” 余额模型&#xff0c;以太坊使用“账户”余额模型。 以太坊丰富了账户内容&#xff0c;除…

AWS Transfer 系列:简化文件传输与管理的云服务

在数字化转型的今天&#xff0c;企业对文件传输、存储和管理的需求日益增长。尤其是对于需要大量数据交换的行业&#xff0c;如何高效、可靠地传输数据成为了一大挑战。为了解决这一难题&#xff0c;AWS 提供了一系列的文件传输服务&#xff0c;统称为 AWS Transfer 系列。这些…

基础I/O -> 如何谈文件与文件系统?

文件的基础理解 空文件也要在磁盘上占据空间。文件 文件内容文件属性。文件操作 对内容的操作 对属性的操作或者是对内容和属性的操作。标定一个文件&#xff0c;必须使用&#xff1a;文件路径 文件名&#xff08;具有唯一性&#xff09;。如果没有指明对应的文件路径&…

网络安全检测

实验目的与要求 (1) 帮助学生掌握木马和入侵的防护和检测方法、提高学习能力、应用能力和解决实际问题的能力。 (2) 要求学生掌握方法, 学会应用软件的安装和使用方法, 并能将应用结果展示出来。 实验原理与内容 入侵检测是通过对计算机网络或计算机系统中若干关键点收集信…

谷歌浏览器的资源管理功能详解

谷歌浏览器作为一款广受欢迎的网页浏览器&#xff0c;不仅以其快速、简洁和易用著称&#xff0c;还提供了强大的资源管理功能。本文将详细介绍如何在Chrome浏览器中进行资源管理&#xff0c;包括查看网页的渲染性能、禁用标签页的背景更新以及管理正在下载的文件。&#xff08;…

ARM异常处理 M33

1. ARMv8-M异常类型及其详细解释 ARMv8-M Exception分为两类&#xff1a;预定义系统异常(015)和外部中断(1616N)。 各种异常的状态可以通过Status bit查看&#xff0c;获取更信息的异常原因&#xff1a; CFSR是由UFSR、BFSR和MMFSR组成&#xff1a; 下面列举HFSR、MMFSR、…

Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉

遇到问题&#xff1a; 从今年开始&#xff0c;不知道咋回事&#xff0c;电脑上的Unity2017像是变了个人似得&#xff0c;突然特别爱闪退掉&#xff0c;有时候还次次闪退&#xff0c;真是让人无语&#xff0c;一直以来我都怀疑是不是电脑上安装了什么别的软件了&#xff0c;导致…

SpringBoot核心:自动配置

有使用过SSM框架的&#xff0c;还记得曾经在spring-mybatis.xml配置了多少内容吗&#xff1f;数据源、连接池、会话工厂、事务管理&#xff0c;而现在Spring Boot告诉你这些都不需要了&#xff0c;简单的几个注解统统搞定&#xff0c;是不是很方便&#xff01; 前言 SpringBoo…

重温设计模式--享元模式

文章目录 享元模式&#xff08;Flyweight Pattern&#xff09;概述享元模式的结构C 代码示例1应用场景C示例代码2 享元模式&#xff08;Flyweight Pattern&#xff09;概述 定义&#xff1a; 运用共享技术有效地支持大量细粒度的对象。 享元模式是一种结构型设计模式&#xff0…

Taro小程序开发性能优化实践

我们团队在利用Taro进行秒送频道小程序的同时&#xff0c;一直在探索性能优化的最佳实践。随着需求的不断迭代&#xff0c;项目中的性能问题难免日积月累&#xff0c;逐渐暴露出来影响用户体验。适逢双十一大促&#xff0c;我们趁着这个机会统一进行了Taro性能优化实践&#xf…

纯血鸿蒙APP实战开发——textOverflow长文本省略

介绍 本示例实现了回复评论时&#xff0c;当回复人的昵称与被回复人的昵称长度都过长时&#xff0c;使用textOverflow和maxLines()实现昵称的长文本省略展示的功能。 效果图预览 使用说明 点击评论中的"回复"&#xff0c;在输入框中输入回复内容&#xff0c;点击发…

【java面向对象编程】第九弹----抽象类、接口、内部类

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;javase &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 一、抽象类 1.1基本介绍 &…

Qt笔记:网络编程UDP

一、铺垫 1.Qt框架使用的网络结构的基础就是Linux学习的网络编程基础&#xff1b;所以使用Qt写客户端&#xff0c;使用Linux写服务端&#xff1b;两者是可以实现互联的 二、UDP 网络编程UDP使用套路&#xff1a; 1.首先在.pro文件中加上network&#xff0c;使Qt可以搭载网络…

Redis存在安全漏洞

Redis是美国Redis公司的一套开源的使用ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值&#xff08;Key-Value&#xff09;存储数据库&#xff0c;并提供多种语言的API。 Redis存在安全漏洞。攻击者利用该漏洞使用特制的Lua脚本触发堆栈缓冲区溢出漏洞&#xff0c;从…

【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题

目录 背包问题简介 问题描述 输入&#xff1a; 输出&#xff1a; 动态规划解法 动态规划状态转移 代码实现 代码解释 动态规划的时间复杂度 例子解析 输出&#xff1a; 总结 作者我蓝桥杯&#xff1a;2023第十四届蓝桥杯国赛C/C大学B组一等奖&#xff0c;所以请听我…

ReactPress 1.6.0:重塑博客体验,引领内容创新

ReactPress 是一个基于Next.js的博客&CMS系统&#xff0c; Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 体验地址&#xff1a;http://blog.gaoredu.com/ 今天&#xff0c;我们自豪地宣布ReactPress 1.6.0版本的正式发布&#xff0c;…

单元测试-Unittest框架实践

文章目录 1.Unittest简介1.1 自动化测试用例编写步骤1.2 相关概念1.3 用例编写规则1.4 断言方法 2.示例2.1 业务代码2.2 编写测试用例2.3 生成报告2.3.1 方法12.3.2 方法2 1.Unittest简介 Unittest是Python自带的单元测试框架&#xff0c;适用于&#xff1a;单元测试、Web自动…

带有 Elasticsearch 和 Langchain 的 Agentic RAG

作者&#xff1a;来自 Elastic Han Xiang Choong 讨论并实现 Elastic RAG 的代理流程&#xff0c;其中 LLM 选择调用 Elastic KB。 更多阅读&#xff1a;Elasticsearch&#xff1a;基于 Langchain 的 Elasticsearch Agent 对文档的搜索。 简介 代理是将 LLM 应用于实际用例的…

[react 3种方法] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? Strongly Type useRef with ElementRef | Total TypeScript import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就…