[db:标题]

摘要:什么是spring的循环依赖?什么是spring的三级缓存?三级缓存怎么解决的循环依赖?解决循环依赖一定要三级缓存吗?spring默认是否支持循环依赖?spring与springboot的区别是什么?
Spring的循环依赖 循环依赖是指在使用Spring框架的过程中,两个或多个Bean之间在初始化的过程相互依赖,形成一个依赖闭环,导致容器无法顺利完成Bean的创建和注入,从而可能引发启动失败或运行异常。 @Service public class ServiceA { @Autowired private ServiceB serviceB; } @Service public class ServiceB { @Autowired private ServiceA serviceA; } 在上面这段代码中: 类 ServiceA 的实例需要注入 ServiceB 的实例。 类 ServiceB 的实例又需要注入 ServiceA 的实例。 这就形成了一个 ServiceA → ServiceB → ServiceA 的循环依赖。 当然大于两个的类也可能存在循环依赖,例如:A→ B→ C→ A 如何解决循环依赖 在Spring中解决循环依赖也是有一定限制的。 循环依赖的Bean都是单例模式的Bean。 依赖注入的方式不能都是构造函数注入的方式。 Spring解决循环依赖为什么只支持单例模式的Bean 从本质上来说Spring解决循环依赖的方式是通过提前暴露未初始化完成的Bean来解决循环依赖的,这个尚未初始化完成的Bean是个半成品的Bean,就是未了解决循环依赖才提前放到缓存中的。 在Spring容器中,单例Bean的创建和初始化只会发生一次,而且是在容器启动的时候就完成的。这就说明在整个容器运行期间,单例Bean的依赖关系不会再发生变化,因此可以在容器启动的时候,通过提前暴露半成品的Bean来一次性解决单例Bean循环依赖的问题。 而由于原型模式(property)的Bean或Session模型下的Bean,创建和初始化会发生多次,并且是在Spring容器运行期间动态的发生变化,因此提前暴露半成品的Bean并不能解决循环依赖的问题,因为在后续的创建过程中,可能会涉及到多个不同的原型Bean,这就无法像单例Bean那样缓存并复用半成品对象。 所以Spring只能自动解决单例Bean的循环依赖。 Spring为什么不支持自动解决构造函数的循环依赖 这个也很好理解,因为在整个Bean的实例化的过程中,是先执行构造函数,只有执行了构造函数后,才算是在堆内存中分配了内存,后面再填充属性,内存地址也不会有变化了。但是如果连构造函数都执行不成功,那这个Bean就连个半成品都不算,所以spring无法解决这种循环依赖。 解决这种构造函数的循环依赖有多种方式 重新设计,从代码层面就把循环依赖给杜绝掉,彻底避免循环依赖。 改成非构造函数依赖,可以采用setter注入或属性注入 使用@Lazy注解解决:使用@Lazy注解时,Spring容器会在实际需要该Bean的时候才会进行实例化,而不是在容器启动的时候进行实例化。这样如果两个Bean存在循环依赖,可以使用这种延迟实例化,从而避免在容器启动时触发循环依赖,但并未真正解决循环依赖本身。 Spring三级缓存 Spring三级缓存是什么? 在Spring框架中,BeanFactory是IOC的基础接口,其中DefaultSingletonBeanRegistry类实现了BeanFactory接口,并维护了三个Map,用来做Bean创建时的三级缓存。 这三级缓存的介绍 一级缓存,singletonObjects 单例缓存 类型:ConcurrentHashMap<String, Object> 作用:存放完全初始化完成的单例Bean实例。 说明:这是最终的单例池,所有已经创建并装配好的Bean都会放在这里,供后续直接获取使用。 二级缓存:earlySingletonObjects 类型:ConcurrentHashMap<String, Object> 作用:存放早期暴露的Bean实例(原始对象,尚未完成属性注入和初始化,半成品)。 当一个Bean正在创建中,但还未完成所有初始化步骤时,可以提前暴露一个“半成品”对象,放入此缓存,供其他Bean引用,从而打破循环依赖。 三级缓存:singletonFactories 类型:ConcurrentHashMap<String, ObjectFactory<?>> 作用:存放能够创建早期Bean实例的工厂对象(ObjectFactory)。 它并不直接存储Bean实例,而是存储一个lambda或匿名内部类,用于在需要时生成早期暴露的对象。
阅读全文