一次偶发项目循环依赖引发的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