首页 > 编程知识 正文

spring多例循环依赖,spring单例循环依赖

时间:2023-05-04 01:40:40 阅读:192388 作者:2126

一次偶发项目循环依赖引发的spring循环依赖简析

spring可以自行帮你解决循环依赖,当然前提是,不能使用构造器注入法来注入属性,否则在项目启动时,spring会直接抛出循环依赖的错误,无法启动。但是当使用@Autowired或者是setter注入时,spring却可以帮你解决循环依赖。那spring到底是如何帮助你解决循环依赖的呢?假如我构造器注入和setter注入混合使用时,spring会不会也乱了呢?

一,问题重现:

一个项目写久了,产品不停的加功能,不停地催促上线,没有时间重构,那在重用某些service方法的时候,就难免会产生循环依赖,而有时,这个依赖圈可能非常之大。由于种种原因,最后构造器注入和@Autowired注入混合使用了。一开始没出现过什么问题,偶然一次出现循环依赖问题了,但是只要重新构建一下项目,就没问题了。随后,这个问题就没断过。直到把所有的构造器注入全部改为@Autowired注入才正常。
代码如下:

public class Grand { private final Enterprise b; public Grand (Enterprise b) { this.b = b; }}public class Enterprise { @Autowired private Person c;}public class Person { @Autowired private Grand a;}

这三个类达成的jar包,有时可以启动成功,有时就会报循环依赖的问题。问题重现了,那到底是因为什么呢?

二,原因剖析:

要剖析原因,首先要知道spring加载bean的顺序,如下图:

这个前提是,所有的类都使用@Autowired注入或者setter注入。但是为什么我之前的代码有时候能启动成功,有时候启动失败呢?
我分析如下:

启动异常

如果构造器注入的Grand要先创建,此时需要Enterprise和Person同时被创建好。首先创建Enterprise,暴露Enterprise工厂,填充Enterprise的属性Person,再创建Person,暴露Person的工厂,填充Person的属性Grand,发现Grand还没有创建,也没有提前暴露工厂,此时spring抛出异常,循环依赖。

启动正常

如果是@Autowired注入的Enterprise先创建,此时暴露工厂,填充属性Person,创建Person,暴露工厂,填充属性Grand,创建Grand发现需要Enterprise和Person,而这两个类都有提前暴露的工厂,则Grand可以顺利通过构造器创建。之后Enterprise和Person被顺利创建,放到单例的缓存中。启动成功。

三,为什么spring加载bean时读取的资源顺序不一样呢?

当使用jar包启动时,spring加载bean的资源顺序取决于jarFile中的顺序,jarFile其实是一种ZipFile,通过文件系统读到对应的资源,其顺序与文件读取的顺序有关。

public void print() throws Exception{ URL rootDirURL = new URL("xxx.jar!/BOOT-INF/classes"); URLConnection con = rootDirURL.openConnection(); JarURLConnection jarCon = (JarURLConnection) con; JarFile jarFile = jarCon.getJarFile(); for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) { JarEntry entry = entries.nextElement(); String entryPath = entry.getName(); System.out.println(entryPath); } }

我将发生异常的jar包和正常运行的jar包,分别读取并打印其资源顺序,果然发现两者顺序并不一致。
至此,构建之后就不会有循环依赖的问题解决了。

四,建议:

尽量提前做好业务规划,服务层之间的关系要理清楚之后再开始开发。若后期有其他的改动,可能导致循环依赖,则需要及时重构,抽象,避免循环依赖的产生。
最好的方式,还是全部都采用构造器注入,虽然增加属性时,写的麻烦一些,但是能在第一时间知晓循环依赖的产生,尽早修改重构。否则到以后,这个项目可能就没办法继续复用方法甚至无法增加新功能。当初偷的懒,到时候都会变成面前的拦路虎。

参考资料:
原来Spring Bean默认加载顺序是这样的
https://mp.weixin.qq.com/s/4avWQm9A9YaS2WC_Rxb0PQ
图解Spring循环依赖,看过之后再也不怕面试被问到了!
https://mp.weixin.qq.com/s/pZjfogfqqtMdFjiFdnMtLg

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。