基于 Springframework 的应用开发,尤其在系统比较复杂时,可能会出现 Bean 循环引用的情形。本文分享一下如何解决这类问题。
正常引用依赖关系:
Bean A → Bean B → Bean C
循环引用依赖关系:
Bean A → Bean B → Bean A
我们知道 Spring 应用在启动时,即会创建 Spring context,加载并实例化 Bean。 正常引用依赖关系中,Spring 尝试实例化 A,发现其依赖 B,则会尝试实例化 B,又发现其依赖 C,则会尝试实例化 C。最终 Spring 会依次创建 bean C,B,A。 而循环引用依赖关系中,Spring 尝试实例化 A,发现其依赖 B,则会尝试实例化 B,又发现其依赖 A,则会尝试实例化 A。最终,Spring 无法决定究竟该先实例化 A 还是先实例化 B。
Spring 在遇到循环引用时,会直接抛出 BeanCurrentlyInCreationException 异常,如:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name &39;: Requested bean is currently in creation: Is there an unresolvable circular reference?
来看一个使用使用构造器注入引发循环引用的例子:
表格中出现循环引用怎么取消,@Componentpublic class BeanOne {private final BeanTwo beanTwo;public BeanOne(BeanTwo beanTwo) {this.beanTwo = beanTwo;}}@Componentpublic class BeanTwo {private final BeanOne beanOne;public BeanTwo(BeanOne beanOne) {this.beanOne = beanOne;}}
BeanOne 和 BeanTwo 相互依赖。
解决循环引用,有多种方法:
使用 Field/Setter 注入
使用 @PostConstruct 注解
1、首先出现循环引用的警告,这种情况需要设置excel启用“迭代计算”,也就是允许使用循环引用。2、接着需要找到“excel选项”,如下图所示:3、其次点击“公式”选项,在“计算选项”中,可以看到“启用迭代计算”前面是没有。
使用 @Lazy 注解
使用 Field/Setter 注入解决循环引用问题
01 首先,我们打开我们电脑上面的excel,然后我们点击文件;02 弹出的界面,我们点击选项;
@Componentpublic class BeanOne {@Autowiredprivate BeanTwo beanTwo;}@Componentpublic class BeanTwo {@Autowiredprivate BeanOne beanOne;}
5、将“启用迭代计算”前面的方框勾选上,然后点击确定 ,这样循环引用的警告的提醒就取消了。
类似的,使用 Setter 注入:
@Componentpublic class BeanOne {private BeanTwo beanTwo;@Autowiredpublic void setBeanTwo(BeanTwo beanTwo) {this.beanTwo = beanTwo;}}@Componentpublic class BeanTwo {private BeanOne beanOne;@Autowiredpublic void setBeanOne(BeanOne beanOne) {this.beanOne = beanOne;}}
这两种方式解决思路是一致的,使用默认的无参构造器实例化 bean,此时无需保证其依赖的 bean 已被实例化。Field 注入本质上和 Setter 注入是一样的。
使用 @PostConstruct 注解
修改示例代码如下:
@Componentpublic class BeanOne {private final BeanTwo beanTwo;public BeanOne(BeanTwo beanTwo) {this.beanTwo = beanTwo;}@PostConstructpublic void init() {beanTwo.setBeanOne(this);}}@Componentpublic class BeanTwo {private BeanOne beanOne;public void setBeanOne(BeanOne beanOne) {this.beanOne = beanOne;}}
1,首先,打开Excel软件之后,在窗口上方的选项卡菜单中点击“公式”选项卡。2,在公式选项卡下,找到“错误检查”一项,点击右侧的箭头图标,打开菜单。3,点击之后,会弹出菜单,在弹出的菜单中依次选择“循环引用”-“单元。
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization...
使用 @PostConstruct 标注的方法,会在依赖注入之后执行,用于某些初始化操作。在这里,这所谓的“初始化”操作,就是为 beanTwo 的 beanOne 属性赋值。
使用 @Lazy 注解
修改代码如下:
@Componentpublic class BeanOne {private final BeanTwo beanTwo;public BeanOne(BeanTwo beanTwo) {this.beanTwo = beanTwo;}}@Componentpublic class BeanTwo {private final BeanOne beanOne;@Lazypublic BeanTwo(BeanOne beanOne) {this.beanOne = beanOne;}}
1.打开一个Excel文件。打开之后,在操作表中的单元格时,出现了循环引用警告。2.点击左上角的 文件 菜单 ,在出现的菜单中点击 选项 。3.然后点击选项。4.然后在打开的页面中点击左侧的 公式 选项。5.在打开的页面中将。
注意到,修改后的代码,与一开始的引发循环引用异常的代码几乎完全相同,差异仅在于 BeanTwo 构造方法上面的 @Lazy 注解。顾名思义,该注解表明这个构造注入懒执行。
查看其源码注释:
Indicates whether a bean is to be lazily initialized.
查看其源码:
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.FIELD})
可见 @Lazy 注解,标示一个 Bean 是否被懒初始化。该注解可用于 类型、方法、构造器、参数、字段等目标,在本例中,用在构造器上,表示只有需要用到该类实例时才进行调用该构造器进行实例化操作。所以在该实例中,可能的实例化过程如下: 分析过程:
Start: 尝试实例化 beanOne → 发现需要依赖 beanTwo → 尝试实例化 beanTwo Start: 尝试实例化 beanTwo → 发现 @Lazy 注解,暂不实例化;
实例化过程:
02 要取消循环引用,我们先把警告对话框关闭;鼠标再点击:文件 03 在弹出的页面中再点:选项;转到excel选项界面,在这里,点击;公式 04 如图所示,勾
实例化 beanTwo → 实例化 beanOne
简言之,使用 @Lazy 标注的类,不会在容器中主动触发实例化,只有当被使用到/被依赖到时,被动触发实例化。
“做程序员,圈子和学习最重要”因为有有了圈子可以让你少走弯路,扩宽人脉,扩展思路,学习他人的一些经验及学习方法!同时在这分享一下是一直以来整理的Java后端进阶笔记文档和学习资料免费分享给大家!需要资料的朋友私信我扣【06】