章
目
录
在使用Spring开发java项目的过程中,经常是使用注解注入,在很多时候不知不觉中就会形成循环依赖,而且一般情况下还是很难发现的,循环依赖会对项目产生一定的不良影响,因此我们需要明白Spring的循环依赖是什么?怎么判断是否产生循环依赖的以及它的解决方式有哪些?
一、什么是循环依赖
首先明白一点,循环依赖并不是说简单的两个类相互依赖对方,比如我们常常在使用mybatis框架时,为了实现一对一的关系,常常在实体类中会相互定义对方,方便由一方查询另一方的信息,在java开发中类的相互依赖是很正常的,比如如下:
// User 依赖了idCard class User{ public IdCard idCard; } // IdCard 依赖了User class IdCard { public User user; }
然而循环依赖一般是指多个bean之间相互依赖,形成了一个闭环。 比如:A依赖于B、B依赖于C、C依赖于A。
通常来说,如果问spring容器内部如何解决循环依赖, 一定是指默认的单例Bean中,属性互相引用的场景。也就是说,Spring的循环依赖,是Spring容器注入时候出现的问题。
比如类似如下代码就会产生循环依赖:
// 第一部分 @Component public class A { @Autowired private B b; } // 第二部分 @Component public class B { @Autowired private C c; } // 第三部分 @Component public class C { @Autowired private A a; }
二、怎么判断产生了循环依赖
判断产生循环依赖,一般很难通过人工审查代码发现,因为每个类可能会引用很多其他类,又比较分散,一般都是在项目联调测试或者发布启动的时候会在日志中打印出来才被发现,也可能在本地运行没问题,在服务器打包就出错,可能这次打包没错,下次打包就有错了,只要有一次出错类似如下图,就说明存在该问题。
但也有个比较好的方法就是,当Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。
三、3种循环依赖的情况
①构造器的循环依赖:这种依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException
异常。
②单例模式下的setter
循环依赖:通过“三级缓存”处理循环依赖,能处理。
③非单例循环依赖:无法处理。原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory
类中下面的判断,抛出异常。
四、Spring循环依赖如何解决
方法一:
- 不使用基于构造函数的依赖注入
- 在字段上使用
@Autowired
注解,让Spring决定在合适的时机注入。【推荐】 - 用基于
setter
方法的依赖注射取代基于构造函数的依赖注入来解决循环依赖。 - 在
@Autowired
注解上方加上@Lazy
注解(延迟加载)
方法二:
使用SpringContextHolder
获取已经存在的bean
在进行解决Spring循环依赖问题时最好先去了解下Spring内部是如何解决循环依赖问题的,这也是面试中经常会被问到的问题,具体的可以去看这篇文章,深入了解下解决循环依赖的底层逻辑。
文章目录 一、针对面试题 二、什么是循环依赖 三、循环依赖的类型 四、Spring中循环依赖的几种场景及Spr […]
五、总结
当然,这里的方案只能针对部分循环依赖的情况,更具体的解决方案还是要了解Spring产生循环依赖的底层逻辑,这样才能更好地对症下药,彻底解决。