spring循环依赖简单理解就是类A中引用B,B引用C,C中引用了A,这样构成了循环。
spring中解决循环依赖主要靠三级本地缓存解决
第一级缓存singletonObjects
里面放置的是缓存实例化好的单例对象。
第二级earlySingletonObjects
里面存放的是提前曝光的单例对象(没有完全装配好)。
第三级singletonFactories
里面存放的是要被实例化的对象的对象工厂
/** Cache of singleton objects: bean name --> bean instance(缓存单例实例化对象的Map集合) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
/** Cache of singleton factories: bean name --> ObjectFactory(单例的工厂Bean缓存集合) */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
/** Cache of early singleton objects: bean name --> bean instance(早期的单身对象缓存集合,未完全事例好) */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Set of registered singletons, containing the bean names in registration order(单例的实例化对象名称集合) */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
具体流程原理参考:https://www.jianshu.com/p/16a44c25c9d9
具体报错的例子是这样的:
QueueMessageCommService通过构造方法注入QueueFieldService,但是QueueFieldService中注入的bean中也注入了QueueMessageCommService,构成了循环。
spring在自动装载时报错beans in the application context form a cycle,与final有关(jdk1.8不同版本有的报错,有的不报错)。
如下图修改后:就不报错了,完全交给spring去装载注入Bean
解决循环依赖的办法:
1、@Autowired 交给spring完全处理
2、setter 单例方式singleton 处理这种方式是bean先实例化后再进行属性填充,这样避免了实例时形成循环
3、setter 原型prototype 代表是有状态的bean,spring 无法完成依赖注入,因为不对它进行缓存(三级缓存的对象)
这样就需要调用Bean中方法(有引用对象使用)之前,先掉set方法赋值(引用对象),防止出现null。
scope="prototype" 意思是 每次请求都会创建一个实例对象。两者的区别是:有状态的bean都使用Prototype作用域,无状态的一般都使用singleton单例作用域。
spring事例化图:
更多setter方式详细见:https://blog.csdn.net/u010644448/article/details/59108799