1 什么是循环依赖?
如下图所示:
BeanA类依赖了BeanB类,同时BeanB类又依赖了BeanA类。这种依赖关系造成了一个闭环,咱们把这种依赖关系就称之为循环依赖。同理,再如下图的状况:
上图中,BeanA类依赖了BeanB类,BeanB类依赖了BeanC类,BeanC类依赖了BeanA类,如此,也造成了一个依赖闭环。再比方:
上图中,本人援用了本人,本人和本人造成了依赖关系。同样也是一个依赖闭环。那么,如果呈现此类循环依赖的状况,会呈现什么问题呢?
2 循环依赖问题复现
2.1 定义依赖关系
咱们持续扩大后面的内容,给ModifyService减少一个属性,代码如下:
@GPServicepublic class ModifyService implements IModifyService { @GPAutowired private QueryService queryService; ...}
给QueryService减少一个属性,代码如下:
@GPService@Slf4jpublic class QueryService implements IQueryService { @GPAutowired private ModifyService modifyService; ... }
如此,ModifyService依赖了QueryService,同时QueryService也依赖了ModifyService,造成了依赖闭环。那么这种状况下会呈现什么问题呢?
2.2 问题复现
咱们来运行调试一下之前的代码,在GPApplicationContext初始化后打上断点,咱们来跟踪一下IoC容器外面的状况,如下图:
启动我的项目,咱们发现只有是有循环依赖关系的属性并没有主动赋值,而没有循环依赖关系的属性均有主动赋值,如下图所示:
这种状况是怎么造成的呢?咱们剖析起因之后发现,因为,IoC容器对Bean的初始化是依据BeanDefinition循环迭代,有肯定的程序。这样,在执行依赖注入时,须要主动赋值的属性对应的对象有可能还没初始化,没有初始化也就没有对应的实例能够注入。于是,就呈现咱们看到的状况。
3 应用缓存解决循环依赖问题
3.1 定义缓存
具体代码如下:
// 循环依赖的标识---以后正在创立的实例bean private Set<String> singletonsCurrectlyInCreation = new HashSet<String>(); //一级缓存 private Map<String, Object> singletonObjects = new HashMap<String, Object>(); // 二级缓存: 为了将成熟的bean和污浊的bean拆散. 防止读取到不残缺的bean.private Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();
3.2 判断循环依赖
减少getSingleton()办法:
/** * 判断是否是循环援用的进口. * @param beanName * @return */ private Object getSingleton(String beanName,GPBeanDefinition beanDefinition) { //先去一级缓存里拿, Object bean = singletonObjects.get(beanName); // 一级缓存中没有, 然而正在创立的bean标识中有, 阐明是循环依赖 if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // 如果二级缓存中没有, 就从三级缓存中拿 if (bean == null) { // 从三级缓存中取 Object object = instantiateBean(beanName,beanDefinition); // 而后将其放入到二级缓存中. 因为如果有屡次依赖, 就去二级缓存中判断. 曾经有了就不在再次创立了 earlySingletonObjects.put(beanName, object); } } return bean; }
3.3 增加缓存
批改getBean()办法,在getBean()办法中增加如下代码:
//Bean的实例化,DI是从而这个办法开始的 public Object getBean(String beanName){ //1、先拿到BeanDefinition配置信息 GPBeanDefinition beanDefinition = regitry.beanDefinitionMap.get(beanName); // 减少一个进口. 判断实体类是否曾经被加载过了 Object singleton = getSingleton(beanName,beanDefinition); if (singleton != null) { return singleton; } // 标记bean正在创立 if (!singletonsCurrentlyInCreation.contains(beanName)) { singletonsCurrentlyInCreation.add(beanName); } //2、反射实例化newInstance(); Object instance = instantiateBean(beanName,beanDefinition); //放入一级缓存 this.singletonObjects.put(beanName, instance); //3、封装成一个叫做BeanWrapper GPBeanWrapper beanWrapper = new GPBeanWrapper(instance); //4、执行依赖注入 populateBean(beanName,beanDefinition,beanWrapper); //5、保留到IoC容器 factoryBeanInstanceCache.put(beanName,beanWrapper); return beanWrapper.getWrapperInstance(); }
3.4 增加依赖注入
批改populateBean()办法,代码如下:
private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) { ... try { //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例 field.set(instance,getBean(autowiredBeanName)); } catch (IllegalAccessException e) { e.printStackTrace(); continue; } ... }
4 循环依赖对AOP创立代理对象的影响
4.1 循环依赖下的代理对象创立过程
咱们都晓得Spring AOP、事务等都是通过代理对象来实现的,而事务的代理对象是由主动代理创立器来主动实现的。也就是说Spring最终给咱们放进容器外面的是一个代理对象,而非原始对象。
这里咱们联合循环依赖,再剖析一下AOP代理对象的创立过程和最终放进容器内的动作,看如下代码:
@Servicepublic class MyServiceImpl implements MyService { @Autowired private MyService myService; @Transactional @Override public Object hello(Integer id) { return "service hello"; }}
此Service类应用到了事务,所以最终会生成一个JDK动静代理对象Proxy。刚好它又存在本人援用本人的循环依赖的状况。跟进到Spring创立Bean的源码局部,来看doCreateBean()办法:
protected Object doCreateBean( ... ){ ... // 如果容许循环依赖,此处会增加一个ObjectFactory到三级缓存外面,以备创建对象并且提前裸露援用 // 此处Tips:getEarlyBeanReference是后置处理器SmartInstantiationAwareBeanPostProcessor的一个办法, // 次要是保障本人被循环依赖的时候,即便被别的Bean @Autowire进去的也是代理对象 // AOP主动代理创立器此办法里会创立的代理对象 // 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) { // 须要提前裸露(反对循环依赖),注册一个ObjectFactory到三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 如果发现自己被循环依赖,会执行下面的getEarlyBeanReference()办法,从而创立一个代理对象从三级缓存转移到二级缓存里 // 留神此时候对象还在二级缓存里,并没有在一级缓存。并且此时能够晓得exposedObject仍旧是原始对象 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); // 通过这两大步后,exposedObject还是原始对象 // 留神:此处是以事务的AOP为例 // 因为事务的AOP主动代理创立器在getEarlyBeanReference()创立代理后, // initializeBean() 就不会再反复创立了,二选一,上面会有详细描述) ... // 循环依赖校验(十分重要) if (earlySingletonExposure) { // 后面讲到因为本人被循环依赖了,所以此时候代理对象还寄存在二级缓存中 // 因而,此处getSingleton(),就会把代理对象拿进去 // 而后赋值给exposedObject对象并返回,最终被addSingleton()增加进一级缓存中 // 这样就保障了咱们容器里缓存的对象实际上是代理对象,而非原始对象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 这个判断不可少(因为initializeBean()办法中给exposedObject对象从新赋过值,否则就是是两个不同的对象实例) if (exposedObject == bean) { exposedObject = earlySingletonReference; } } ... } }
以上代码剖析的是代理对象有本人存在循环依赖的状况,Spring用三级缓存很奇妙的进行解决了这个问题。
4.2 非循环依赖下的代理对象创立过程
如果本人并不存在循环依赖的状况,Spring的处理过程就略微不同,持续跟进源码:
protected Object doCreateBean( ... ) { ... addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... // 此处留神,因为没有循环援用,所以下面getEarlyBeanReference()办法不会执行 // 也就是说此时二级缓存里并不会存在 populateBean(beanName, mbd, instanceWrapper); // 重点在此 //AnnotationAwareAspectJAutoProxyCreator主动代理创立器此处的postProcessAfterInitialization()办法里,会给创立一个代理对象返回 // 所以此局部执行实现后,exposedObject() 容器中缓存的曾经是代理对象,不再是原始对象 // 此时二级缓存里仍旧无它,更别提一级缓存了 exposedObject = initializeBean(beanName, exposedObject, mbd); ... // 循环依赖校验 if (earlySingletonExposure) { // 后面讲到一级、二级缓存里都没有缓存,而后这里传参数是false,示意不从三级缓存中取值 // 因而,此时earlySingletonReference = null ,并间接返回 // 而后执行addSingleton()办法,由此可知,容器里最终存在的也还是代理对象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } } ...}
依据以上代码剖析可知,只有用到代理,没有被循环援用的,最终存在Spring容器里缓存的仍旧是代理对象。如果咱们敞开Spring容器的循环依赖,也就是把allowCircularReferences设值为false,那么会不会呈现问题呢?先敞开循环依赖开关。
// 它用于敞开循环援用(敞开后只有有循环援用景象将报错)@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(false); }}
敞开循环依赖后,下面代码中存在A、B循环依赖的状况,运行程序会呈现如下异样:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
此处异样类型也是BeanCurrentlyInCreationException异样,但报错地位在DefaultSingletonBeanRegistry.beforeSingletonCreation
咱们来剖析一下,在实例化A后给其属性赋值时,Spring会去实例化B。B实例化实现后会持续给B属性赋值,因为咱们敞开了循环依赖,所以不存在提前裸露援用。因而B无奈间接拿到A的援用地址,只能又去创立A的实例。而此时咱们晓得A其实曾经正在创立中了,不能再创立了。所有就呈现了异样。对照演示代码,来剖析一下程序运行过程:
@Servicepublic class MyServiceImpl implements MyService { // 因为敞开了循环依赖,所以此处不能再依赖本人 // 然而MyService须要创立AOP代理对象 //@Autowired //private MyService myService; @Transactional @Override public Object hello(Integer id) { return "service hello"; }}
其大抵运行步骤如下:
protected Object doCreateBean( ... ) { // earlySingletonExposure = false 也就是Bean都不会提前裸露援用,因而不能被循环依赖 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); ... populateBean(beanName, mbd, instanceWrapper); // 若是开启事务,此处会为原生Bean创立代理对象 exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) { ... // 因为下面没有提前裸露代理对象,所以下面的代理对象exposedObject间接返回。 }}
由下面代码可知,即便敞开循环依赖开关,最终缓存到容器中的对象仍旧是代理对象,显然@Autowired给属性赋值的也肯定是代理对象。
最初,以AbstractAutoProxyCreator为例看看主动代理创立器实现循环依赖代理对象的细节。
AbstractAutoProxyCreator是抽象类,它的三大实现子类InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator小伙伴们应该比拟相熟,该抽象类实现了创立代理的动作:
// 该类实现了SmartInstantiationAwareBeanPostProcessor接口 ,通过getEarlyBeanReference()办法解决循环援用问题public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { ... // 上面两个办法是主动代理创立器创立代理对象的唯二的两个节点: // 提前裸露代理对象的援用,在postProcessAfterInitialization之前执行 // 创立好后放进缓存earlyProxyReferences中,留神此处value是原始Bean @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } // 因为它会在getEarlyBeanReference之后执行,这个办法最重要的是上面的逻辑判断 @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 上面的remove()办法返回被移除的value,也就是原始Bean // 判断如果存在循环援用,也就是执行了下面的getEarlyBeanReference()办法, // 此时remove() 返回值必定是原始对象 // 若没有被循环援用,getEarlyBeanReference()不执行 // 所以remove() 办法返回null,此时进入if执行逻辑,调用创立代理对象办法 if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ...}
依据以上剖析可得悉,主动代理创立器它保障了代理对象只会被创立一次,而且反对循环依赖的主动注入的仍旧是代理对象。由下面剖析得出结论,在Spring容器中,不管是否存在循环依赖的状况,甚至敞开Spring容器的循环依赖性能,它对Spring AOP代理的创立流程有影响,但对后果是无影响的。也就是说Spring很好地屏蔽了容器中对象的创立细节,让使用者齐全无感知。
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!
如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货!