探索Spring循环依赖的细节

22 篇文章 0 订阅

之前在Spring学习笔记,挺全的!这个博客中提到了Spring对循环依赖的解决原理,这篇博客对一些细节做个补充。

背景回顾

@Service
public class LagouBean {
  @Autowired
  private ItBean itBean;
  
  public ItBean getItBean() {
        return itBean;
    }
}

@Service
public class ItBean {
  @Autowired
  private LagouBean lagouBean;
}

LagouBean被SpringAOP增强(代码省略)。

在这里插入图片描述

之前只是扔出了这张图片,现在来补充一下文字说明。

循环依赖解决步骤:

1)实例化lagouBean,然后将其放入三级缓存singletonFactories,key是beanName,value是一个对象工厂ObjectFactory

new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}

2)对lagouBean进行属性注入,发现其依赖了itBean

3)实例化itBean,然后将其放入三级缓存,key是beanName,value是一个对象工厂ObjectFactory

new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}

4)对itBean进行属性注入,发现其依赖于lagouBean

5)通过依赖解析调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference),beanName此时传递“lagouBean”,allowEarlyReference传递true。

从三级缓存singletonFactories拿到lagouBean的对象工厂ObjectFactory,调用其getObject方法,进而调用getEarlyBeanReference。

6)在getEarlyBeanReference里面会遍历所有的BeanPostProcessor找到特定类型的BeanPostProcessor(SmartInstantiationAwareBeanPostProcessor),调用其getEarlyBeanReference方法,会对发生【循环依赖】的bean即这里的lagouBean暴露一个“早期引用”,如果该bean需要进行SpringAOP增强,那么暴露的是一个该bean即这里的lagouBean的代理类的引用,同时将这个代理类对象放到二级缓存earlySingletonObjects中

7)将暴露的lagouBean的代理类注入到itBean,itBean完成创建以后会从三级缓存singletonFactories中移除,并将其放到一级缓存singletonObjects中

8)将创建完成的itBean注入到lagouBean,然后进行后置处理逻辑(BeanPostProcessor等逻辑)并返回处理完以后的lagouBean,如果处理完以后的lagouBean和处理之前的lagouBean一样(是同一个对象),从二级缓存earlySingletonObjects获取lagouBean对应的值,经过步骤6,将会获取到lagouBean的代理类对象,然后把该代理类对象返回

9)将步骤8返回的lagouBean代理类对象放到一级缓存singletonObjects中(key为“lagouBean”,value为lagouBean代理类对象)

此时lagouBean和itBean的引用关系如下:
在这里插入图片描述

细节探索

注意到上面的步骤8,对应的部分源码如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java

//bean是刚刚实例化的对象
Object exposedObject = bean;
//1、属性注入
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
  //2、对bean进行BeanPostProcessor等处理,SpringAOP动态代理也在此处生成,返回一个代理对象
		exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//3、从二级缓存获取早期引用即lagouBean的代理对象
Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
        //4、判断是否在initializeBean过程中生成代理对象并返回
				if (exposedObject == bean) {
          //5、将exposedObject指向lagouBean的代理对象
					exposedObject = earlySingletonReference;
				}
        //6、如果发现有已经创建完成的bean依赖了beanName对应的bean,并且这个依赖的bean和exposedObject不相同,就报循环依赖异常
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
//7、返回
return exposedObject;

应该会走上面的5处的代码,就说明initializeBean方法执行完成返回的bean和参数传入的bean是一样的!

为什么一样

根据示例代码,对LagouBean是要进行SpringAOP增强的,那应该会在initializeBean中执行相应的BeanPostProcessor生成代理对象才是啊?

继续分析源码,lagoBean在进行属性注入以后会进入initializeBean,然后找到SpringAOP相关的BeanPostProcessor即AbstractAutoProxyCreator,其postProcessAfterInitialization方法如下:

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.java

@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //重点关注,earlyProxyReferences不包含当前beanName的时候才去走生成代理类的逻辑
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

可以看到在earlyProxyReferences不包含当前beanName的时候才去走生成代理类的逻辑,否则直接原样返回。

还记得循环依赖解决的步骤6吗?部分源码如下:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.java

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
          //获取代理对象
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
            //放入二级缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

其中ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName)会走到

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
          //获取代理对象
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
					if (exposedObject == null) {
						return null;
					}
				}
			}
		}
		return exposedObject;
	}

其中exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);会走到

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.java

public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
	//1、根据beanName信息获取cacheKey	
  Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
      //2、将cacheKey放到earlyProxyReferences
			this.earlyProxyReferences.add(cacheKey);
		}
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
      //3、根据beanName信息获取cacheKey
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //4、earlyProxyReferences不包含当前beanName的时候才去走生成代理类的逻辑
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

可以看到在这里将参数传递的bean对应的cacheKey放入了earlyProxyReferences,

代码2和代码4正好呼应上,整体过程如下:

在这里插入图片描述

话又说回来了,上面分析的是initializeBean方法执行完成返回的bean和参数传入的bean为什么是一样的,那什么情况下不一样,从而抛出循环依赖异常呢?

什么情况下会不一样

还是LagouBean和ItBean的例子,只不过对LagouBean不使用Aspect来增强,而是通过自定义BeanPostProcessor来生成代理对象,代码如下:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        if ("lagouBean".equals(s)) {
            return Proxy.newProxyInstance(o.getClass().getClassLoader(),
                    o.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        System.out.println("----开始");
                        Object o1 = method.invoke(o,args);
                        System.out.println("----结束");
                        return o1;
                    });
        } else {
            return o;
        }
    }
}

重新启动spring发现报错

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'lagouBean': Bean with name 'lagouBean' has been injected into other beans [itBean] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

继续跟踪源码发现是因为initializeBean方法执行完成返回的bean和参数传入的bean不一样了。

因为在执行了initializeBean之后返回了由自定义MyBeanPostProcessor生成的lagouBean的代理对象,此时lagouBean最终对应的是一个lagouBean的代理对象,而当初itBean注入lagouBean的时候注入的是lagouBean的原始对象,这就产生了矛盾,继续往下执行就会抛出上面的异常。

lagouBean.getItBean()问题

再来回顾一下上面循环依赖解决之后lagouBean和itBean的引用关系

在这里插入图片描述

可以看到其实lagouBean对应的是一个lagouBean的代理对象,而这个代理对象中的itBean属性是null!

那lagouBean.getItBean()岂不是获取到的是个null?经过测试事实并不会返回null,而是正常返回了itBean的对象。

那继续来看看源码到底发生了什么

首先给lagouBean.getItBean()这行代码打断点发现进入的并不是LagouBean本身的getItBean()方法,而是进入了org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor.intercept方法,从前面我们知道lagouBean的代理对象是通过cglib产生的,对cglib感兴趣的可以参考Cglib原理解析

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor正好实现了MethodInterceptor接口,所以对于cglib代理类方法的调用就会进入该类的intercept方法,部分源码如下:

org.springframework.aop.framework.CglibAopProxy.java

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  ...
  retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
	...
}

最终会进入到

org.springframework.aop.framework.ReflectiveMethodInvocation.java

public Object proceed() throws Throwable {
		//	We start with an index of -1 and increment early.
  //遍历interceptorsAndDynamicMethodMatchers
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      //反射调用被代理类的原始方法
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		...
      //执行增强逻辑
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  	...
}

可以看到依次执行interceptorsAndDynamicMethodMatchers中保存的拦截器的invoke方法来完成增强逻辑(这种设计思路可以参考实现一个增强版的JDK动态代理来简单理解),执行完以后会通过反射去调用被代理的目标类的原始的目标方法,返回上面lagouBean和itBean依赖关系中target所依赖的itBean对象。


相关代码请参考:https://gitee.com/xujian01/blogcode/tree/master/springcode/src/main/java/com/jarry/circledepency

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值