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-1,fun方法开始处理业务

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

posted on 2024-02-27 14:49  清清飞扬  阅读(450)  评论(0编辑  收藏  举报