随笔 - 493  文章 - 0  评论 - 97  阅读 - 239万

springboot2.6开始禁止循环依赖了

参考文章: https://mp.weixin.qq.com/s?__biz=MzI0MTUwOTgyOQ==&mid=2247497189&idx=1&sn=0f03cdafad9bacef66c64a490b85ff23&scene=21#wechat_redirect

使用了SpringBoot2.6及以上版本的,如果要允许循环依赖,可以作如下设置 :

方案二:允许循环引用

此方案更像是绕过问题而非解决问题本身!!!

它是一种妥协方案而非最佳实践。在Spring Boot 2.6.0之前版本无需担心此问题(默认允许循环引用),若你准备使用2.6.x但现实情况依旧必须允许循环引用那该怎么办呢?

有哪些现实情况呢?诸如:老项目升级Spring Boot版本需要保持向下兼容性;公司coder的水平不一,强制高标准的要求将会严重影响到生产效率等等

为此,做法只有一个:禁用默认行为(允许循环引用)。具体做法也很简单,其实在文上启动失败的报错详情里Spring Boot已非常贴心的告诉你了:图片所以只需在配置文件application.properties里加上这个属性:

spring.main.allow-circular-references = true

再次启用Spring Boot 2.6.0版本的应用:正常启动。

除了加属性这个方法之外,也可以通过启动类API的方式来设置,能达到同样效果:

public static void main(String[] args) {
    new SpringApplicationBuilder(Application.class)
            .allowCircularReferences(true// 允许循环引用
            .run(args);
}

图片我们知道,允许循环引用与否其实是Spring Framework的能力,Spring Boot只是将其暴露为属性参数方便开发者来控制而已。那么问题来了,如果是一个构建在纯Spring Framework上的应用,如何禁止循环引用呢?你知道怎么做吗?欢迎在留言区讨论作答,或私聊我探讨学习~


加餐:允许循环引用了但依旧报错

也许你一直认为Spring已经解决循环引用问题了,所以在使用过程中可以“毫无顾忌”。非也,某些“特殊”场景下可能依旧会碰壁,并且问题还很隐蔽不好定位,不信你看我层层递进的给你描述这个场景:

说明:以下代码在允许循环引用的Spring Boot场景下演示运行

基础代码:

本例使用@PostConstruct来模拟触发方法调用,效果和Controller里调Service方法一样哈

@Service
public class AService {

    @PostConstruct
    private void init() {
        String threadName = Thread.currentThread().getName();
        System.out.printf("线程号为%s,开始调用业务fun方法\n", threadName);
        fun();

    }

    public void fun() {
        String threadName = Thread.currentThread().getName();
        System.out.printf("线程号为%s,开始处理业务\n", threadName);
    }

}

启动应用即触发动作,控制台输出为:

线程名为main,开始调用业务fun方法
线程名为main,fun方法开始处理业务

完美!此时,你发现fun方法执行时间太长,需要做异步化处理。你就立马想到了使用Spring提供的@Async注解轻松搞定:

@Async
public void fun() {
 ...
}

再次运行,控制台输出:

线程名为main,开始调用业务fun方法
线程名为main,fun方法开始处理业务

what?木有生效呀!这时你灵机一动,原因是没用开启该模块嘛。所以你迅速的使用@EnableAsync注解启用Spring的异步模块,满怀期待的再次运行应用,控制台输出:

线程名为main,开始调用业务fun方法
线程名为main,fun方法开始处理业务

what a ...?怎么还是不行。你挠了挠头,想起来之前踩过的“事务不生效的坑”,场景和这类似,所以你模仿着采用了相同的方式来解决:自己注入自己(循环依赖)

@Autowired
private AService aService; // 自己注入自己

@PostConstruct
private void init() {
 ...
    aService.fun(); // 通过代理对象调用而非this调用

}

这次满怀信心的再次运行,没想到,启动抛出BeanCurrentlyInCreationException异常

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [AService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:649) ~[spring-beans-5.3.13.jar:5.3.13]
 ...

异常关键字:circular reference循环引用!!!不是说好了允许循环引用的吗?怎么肥四?怎么破???

至此,笔者将此问题抛出,有兴趣的同学可思考一下问题根因、解决方案哈。最终的效果应该是不同线程异步执行的:

线程名为main,开始调用业务fun方法
线程名为task-1fun方法开始处理业务

Tips:笔者在之前的文章里对此问题有过非常非常详细的叙述,感兴趣的可自行向前翻哈!!!主动学习😄

posted on   清清飞扬  阅读(1231)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2017-02-27 Redis系列之key操作命令与Redis中的事务详解(六)
2011-02-27 c++ 标准库MAP用法
2011-02-27 c++ map的使用--键值对的集合
2011-02-27 vbs删除sFolder目录下,nDay之前的日志
2011-02-27 vbs根据文件名取得文件Title(文件名中不含扩展名的部分)
2011-02-27 vbscript时间函数
2011-02-27 vbs获取目录下的文件和文件夹集合
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示