太原适合网站设计地址,网站建设 教学视频教程,保定网站定制公司,网站布局先看几个问题 什么事循环依赖#xff1f;什么情况下循环依赖可以被处理#xff1f;spring是如何解决循环依赖的#xff1f;
什么是循环依赖#xff1f;
简单理解就是实例 A 依赖实例 B 的同时 B 也依赖了 A Component
public class A {// A 中依赖 BAutowiredprivate B b… 先看几个问题 什么事循环依赖什么情况下循环依赖可以被处理spring是如何解决循环依赖的
什么是循环依赖
简单理解就是实例 A 依赖实例 B 的同时 B 也依赖了 A Component
public class A {// A 中依赖 BAutowiredprivate B b;
}Component
public class B {// B 中依赖 AAutowiredprivate A a;
}
什么情况下循环依赖可以被处理 spring 解决循环依赖是有前提条件的 出现循环依赖的 bean 必须是单例的依赖注入的方式不能全是构造器注入的方式 其中第一点是很好理解的第二点不能全是构造器注入是什么意思呢用代码说话
Component
public class A {// A 中依赖 Bpublic A(B b){}
}Component
public class B {// B 中依赖 Apublic B(A a){}
}为了测试循环依赖的解决情况跟注入方式的关系我们做如下四种情况的测试
Spring是如何解决循环依赖的
分两种情况进行说明
简单的循环依赖没有AOP含有AOP的循环依赖
简单的循环依赖没有AOP
还是使用上面的例子
Component
public class A {// A 中依赖 BAutowiredprivate B b;
}Component
public class B {// B 中依赖 AAutowiredprivate A a;
}通过前面我们知道这种循环依赖是可以解决的下面进行分析
首先我们都知道Spring在创建Bean的时候主要有三步
实例化对应方法 AbstractAutowireCapableBeanFactory#createBeanInstance实例化之后只是在堆中创建了实例实例中属性都为默认值然后放入到三级缓存之中属性注入对应方法AbstractAutowireCapableBeanFactory#populateBean为实例化之后的对象进行属性填充初始化对应方法AbstractAutowireCapableBeanFactory#initializeBean执行初始化方法之后在实现了BeanPostProcessor的postProcessAfterInitialization完成AOP代理 创建A对象
getSingleton() public Object getSingleton(String beanName, ObjectFactory? singletonFactory) {// beanName 断言处理Assert.notNull(beanName, Bean name must not be null);// 对一级缓存加锁处理synchronized (this.singletonObjects) {// 从一级缓存中获取Object singletonObject this.singletonObjects.get(beanName);if (singletonObject null) {// 一级缓存中获取不到if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!));}if (logger.isDebugEnabled()) {logger.debug(Creating shared instance of singleton bean beanName );}// 此处是在单例bean创建之前, 判断bean是否需要检查// 并且将beanName添加到singletonsCurrentlyInCreation(正在创建bean的集合是一个setFromMap集合)中beforeSingletonCreation(beanName);boolean newSingleton false;boolean recordSuppressedExceptions (this.suppressedExceptions null);if (recordSuppressedExceptions) {this.suppressedExceptions new LinkedHashSet();}try {// 此处是一个回调回去执行createBean()方法,也就是开始真正的创建beansingletonObject singletonFactory.getObject();newSingleton true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime -// if yes, proceed with it since the exception indicates that state.singletonObject this.singletonObjects.get(beanName);if (singletonObject null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions null;}// 至此beanName创建完毕从singletonsCurrentlyInCreation(正在创建的集合)中移除afterSingletonCreation(beanName);}if (newSingleton) {// 将成品的bean添加到一级缓存并从二级缓存、三级缓存中移除并添加到已完成注册的单例bean集合中addSingleton(beanName, singletonObject);}}return singletonObject;}}从上面我们可以看到spring在创建一个bean时先是调用 getBean() - doGetBean() - getSingleton() 主要的处理逻辑就在getSingleton()之中从getSingleton()源码中我们可以看到 1、先从一级缓存获取A2、获取不到再执行回调去创建A 下面接着调用回调方法 createBean() 去创建 A
大致流程如下本质就是使用反射创建A对象实例 注意需要注意在 createBeanInstance() 方法中先调用 instantiateBean() 方法创建bean实例对象创建完毕以后会接着调用 addSingletonFactory()方法下面我们分析一下这个方法
addSingletonFactory 可以看到 earlySingletonExposure 为 true就会将 创建出来的A对象实例对象放入到三级缓存之中
protected void addSingletonFactory(String beanName, ObjectFactory? singletonFactory) {// 对 singletonFactory 进行非空校验Assert.notNull(singletonFactory, Singleton factory must not be null);// 对一级缓存进行加锁synchronized (this.singletonObjects) {// 如果一级缓存中不存在 beanName 对应的单例对象if (!this.singletonObjects.containsKey(beanName)) {// 将 singletonFactory 添加到三级缓存中this.singletonFactories.put(beanName, singletonFactory);// 将 beanName 从二级缓存中移除this.earlySingletonObjects.remove(beanName);// 将 beanName 添加到 registeredSingletons 中this.registeredSingletons.add(beanName);}}}可以看出这里放入三级缓存中的是一个 ObjectFactory 这个工厂的 getObject()方法可以得到一个对象而这个对象是由 getEarlyBeanReference() 创建的那么问题来了这个 getEarlyBeanReference() 方法什么时候被调用呢在创建B对象的时候 接着往下看
三级缓存放入完毕然后对A进行属性填充大致流程如下这里不做详细分析 一句话概括就是对A进行属性填充的时候发现A中依赖了B对象就调用 this.beanFactory.getBean()方法获取B对象实例也就是套娃模式开启
又开始重复上述流程去创建B对象实例流程如下 此时 B 对象创建完毕三级缓存中也有了 B 对象的工厂然后对 B 对象进行属性填充流程与A类似属性填充过程中发现B对象中依赖A对象又调用 this.beanFactory.getBean(A)下面分析getSingleton()方法 注意此处 getSingleton()方法与前面介绍的getSingleton()方法不同前面介绍的getSingleton()方法是在本次getSingleton()方法执行完毕未获取到结果之后才会执行前面讲解的getSingleton()方法
Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock// 从一级缓存中获取该beanName实例Object singletonObject this.singletonObjects.get(beanName);// 如果以及缓存中不存在并且该beanName对应的单例bean正在创建中if (singletonObject null isSingletonCurrentlyInCreation(beanName)) {// 从二级缓存中获取该beanName实例singletonObject this.earlySingletonObjects.get(beanName);// 二级缓存中不存在并且允许提前引用if (singletonObject null allowEarlyReference) {// 锁定全局变量进行操作synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton lock// 从一级缓存中获取该beanName实例singletonObject this.singletonObjects.get(beanName);// 如果一级缓存中获取不到if (singletonObject null) {// 从二级缓存中获取singletonObject this.earlySingletonObjects.get(beanName);// 二级缓存中也获取不到if (singletonObject null) {// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的 ObjectFactory 初始化策略存储在 singletonFactoriesObjectFactory? singletonFactory this.singletonFactories.get(beanName);if (singletonFactory ! null) {// 如果存在单例对象工厂则通过工厂创建一个单例对象singletonObject singletonFactory.getObject();// 放入到二级缓存中this.earlySingletonObjects.put(beanName, singletonObject);// 并从三级缓存中移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}从源码中可以看到getSingleton()方法会先从一级缓存中获取A对象实例如果获取不到再从二级缓存中获取二级缓存中获取不到再从三级缓存中那么此时三级缓存中肯定是可以获取到的获取到之后调用 getObject() 方法此时调用 getObject()方法会回调 getEarlyBeanReference() 方法 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {// 该方法主要用于提前获取 bean 的引用以便于解决循环依赖的问题// 将当前 bean 赋值给 exposedObjectObject exposedObject bean;if (!mbd.isSynthetic() hasInstantiationAwareBeanPostProcessors()) {// 这块代码是用代理对象替换原始对象这样就可以在原始对象的基础上做一些增强操作for (BeanPostProcessor bp : getBeanPostProcessors()) {// AOP -- AnnotationAwareAspectJAutoProxyCreatorif (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;}从源码可以看出实际上就是调用了后置处理器的 getEarlyBeanReference而真正实现了这个方法的后置处理器只有一个就是通过EnableAspectJAutoProxy 注解导入的 AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下上面的代码等价于 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject bean;return exposedObject;}这样的话也就是说这个工厂啥也没干直接将实例化阶段创建的对象返回了
所以说在不考虑AOP的情况下三级缓存有用嘛讲道理真的没什么用我直接将这个对象放到二级缓存中不是一点问题都没有吗如果你说它提高了效率那你告诉我提高的效率在哪?
那么三级缓存到底有什么作用呢不要急我们先把整个流程走完在下文结合AOP分析循环依赖的时候你就能体会到三级缓存的作用
到这里不知道小伙伴们会不会有疑问B中提前注入了一个没有经过初始化的A类型对象不会有问题吗
答不会
这个时候我们需要将整个创建A这个Bean的流程走完如下图
从上图中我们可以看到虽然在创建B时会提前给B注入了一个还未初始化的A对象但是在创建A的流程中一直使用的是注入到B中的A对象的引用之后会根据这个引用对A进行初始化所以这是没有问题的。
创建B对象
从前面我们已经知道在创建A的过程中已经把B对象创建好了而且已经放入到了一级缓存但是spring是通过循环遍历beanName去创建bean实例的所以B还会在创建一次与创建A对象的区别在于在创建B对象的过程中在调用getSingleton()方法的时候可以从一级缓存中直接拿到B对象所以直接返回不在进行创建
至此没有AOP的循环依赖就到此为止下面继续看有AOP的循环依赖
AOP循环依赖
之前我们已经说过了在普通的循环依赖的情况下三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP相关我们再来看一看getEarlyBeanReference()方法的代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {// 该方法主要用于提前获取 bean 的引用以便于解决循环依赖的问题// 将当前 bean 赋值给 exposedObjectObject exposedObject bean;if (!mbd.isSynthetic() hasInstantiationAwareBeanPostProcessors()) {// 这块代码是用代理对象替换原始对象这样就可以在原始对象的基础上做一些增强操作for (BeanPostProcessor bp : getBeanPostProcessors()) {// AOP -- AnnotationAwareAspectJAutoProxyCreatorif (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;}如果在开启AOP的情况下就会调用 AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference()方法 public Object getEarlyBeanReference(Object bean, String beanName) {// 根据bean的类型和名称获取缓存的key如果beanName为空则使用bean的类型作为key// 如果beanName不为空则使用beanName作为key如果beanName是一个FactoryBean的名称则使用beanName作为keyObject cacheKey getCacheKey(bean.getClass(), beanName);// 添加到earlyProxyReferences中this.earlyProxyReferences.put(cacheKey, bean);// 创建aop代理return wrapIfNecessary(bean, beanName, cacheKey);}从代码可以看出如果我们对A进行了AOP代理的话那么此时的getEarlyBeanReference()方法将返回一个A的代理对象而不是实例化阶段创建的A对象这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。 看到这个图你可能会产生下面这些疑问
在给B注入的时候为什么要注入一个代理对象
答当我们对A进行了AOP代理时说明我们希望从容器中获取到的就是A代理后的对象而不是A本身因此把A当作依赖进行注入时也要注入它的代理对象
明明初始化的时候是A对象那么Spring是在哪里将代理对象放入到容器中的呢
在完成初始化的时候spring会在调用一次getSingleton()方法这一次传入的参数又不一样了false可以理解为禁用三级缓存前面说过B进行属性填充的时候已经从三级缓存中获取到A对象然后生成A的代理对象并将代理对象放入到二级缓存中所以在A完成初始化的时候所以再从二级缓存中获取到A代理对象赋值给 exposedObject最终放入到一级缓存中 初始化的时候是对A对象本身进行初始化而容器中以及注入到B中的都是代理对象这样不会有问题吗
答不会这是因为不管是cglib代理还是jdk动态代理生成的代理类内部都持有一个目标类的引用当调用代理对象的方法时实际会去调用目标对象的方法A完成初始化相当于代理对象自身也完成了初始化
三级缓存为什么要使用工厂而不是直接使用引用换而言之为什么需要这个三级缓存直接通过二级缓存暴露一个引用不行吗
答这个工厂的目的在于延迟创建对实例化阶段生成的对象的代理只有真正发生循环依赖的时候才去提前生成代理对象否则只会创建一个工厂并将其放入到三级缓存中但是不会去通过这个工厂去真正创建对象
我们思考一种简单的情况就以单独创建A为例假设AB之间现在没有依赖关系但是A被代理了这个时候当A完成实例化后还是会进入下面这段代码 // Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure (mbd.isSingleton() this.allowCircularReferences isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace(Eagerly caching bean beanName to allow for resolving potential circular references);}// 将创建的bean 的 lambda 表达式放入到三级缓存中addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean));}假设我们在这里直接使用二级缓存的话那么意味着所有的Bean在这一步都要完成AOP代理。这样做有必要吗
不仅没有必要而且违背了Spring在结合AOP跟Bean的生命周期的设计Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖那没有办法只有给Bean先创建代理但是没有出现循环依赖的情况下设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
三级缓存真的提高了效率了吗
通过以上分析我们已经知道了三级缓存的真正作用但是这个答案可能还无法说服你所以我们再最后总结分析一波三级缓存真的提高了效率了吗分为两点讨论
没有进行AOP的Bean间的循环依赖
从上文分析可以看出这种情况下三级缓存根本没用所以不会存在什么提高了效率的说法
进行了AOP的Bean间的循环依赖
就以我们上的A、B为例其中A被AOP代理我们先分析下使用了三级缓存的情况下A、B的创建流程 假设不使用三级缓存直接使用二级缓存
上面两个流程的唯一区别在于为A对象创建代理的时机不同在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言消耗的时间是一样的
综上不管是哪种情况三级缓存提高了效率这种说法都是错误的
总结
“Spring是如何解决的循环依赖”
答Spring通过三级缓存解决了循环依赖其中一级缓存为单例池singletonObjects,二级缓存为早期曝光对象earlySingletonObjects三级缓存为早期曝光对象工厂singletonFactories。当A、B两个类发生循环引用时在A完成实例化后就使用实例化后的对象去创建一个对象工厂并添加到三级缓存中如果A被AOP代理那么通过这个工厂获取到的就是A代理后的对象如果A没有被AOP代理那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时会去创建B同时B又依赖了A所以创建B的同时又会去调用getBean(a)来获取需要的依赖此时的getBean(a)会从缓存中获取第一步先获取到三级缓存中的工厂第二步调用对象工工厂的getObject方法来获取到对应的对象得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程包括初始化、后置处理器等。当B创建完后会将B再注入到A中此时A再完成它的整个生命周期。至此循环依赖结束
“为什么要使用三级缓存呢二级缓存能解决循环依赖吗”
答如果要使用二级缓存解决循环依赖意味着所有Bean在实例化后就要完成AOP代理这样违背了Spring设计的原则Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理而不是在实例化后就立马进行AOP代理。