之前有篇章有介绍过spring是如何解决循环依赖Spring源码解析(7)之循环依赖解决_jokeMqc的博客-CSDN博客的,但是我看了之后感觉说的比较的简单,这里再结合spring的源码,说明spring是如何解决循环依赖的。
一、什么是循环依赖
所谓的循环依赖就是A依赖B,B依赖A,或者是A依赖B,B依赖C,C依赖A。
代码实例:
getter/setter
public class InstanceA {
private InstanceB instanceB;
}
public class InstanceB {
private InstanceA instanceA;
}
<bean id="instanceA" class="com.tuling.circulardependencies.InstanceA" >
<constructor-arg name="instanceB" ref="instanceB"></constructor-arg>
</bean>
<bean id="instanceB" class="com.tuling.circulardependencies.InstanceB" >
<constructor-arg name="instanceA" ref="instanceA"></constructor-arg>
</bean>
可能存在的问题:
IOC容器在创建Bean的时候,按照顺序,先去实例化instanceA。然后突然发现我的instanceA是依赖我 的instanceB的。那么IOC容器接着去实例化intanceB,那么在intanceB的时候发现依赖instanceA。若容器不处理的, 那么IOC 将无限的执行。上述流程。直到内存异常程序奔溃.
解决方案:当然,Spring 是不会让这种情况发生的。在容器发现 beanB 依赖于 beanA 时,容器会获取 beanA 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanB 中,让 beanB 先完成实例化。beanB 完成实例化,beanA 就可以获取到 beanB 的引用,beanA 随之完成实例化。这里大家可 能不知道“早期引用”是什么意思,这里先别着急。
1.1什么是早期引用 初始化好的Bean 先来看下如下代码调用链
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 去缓存map中获取已经实例化的对象
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);
}
我熟知spring解决循环依赖是通过三级缓存来解决的,下面我们介绍一下这三个缓存各个的作用是啥。
//用于存放 beanName和 初始化好的bean对象(属性已经初始化好的)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
//存放 bean 工厂对象,用于解决循环依赖
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
// 用于存放beanName 和一个原始bean 早期bean(属性未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
1.2 Bean加载整个过程调用链
i0>org.springframework.beans.factory.support.AbstractBeanFactory#getBean
i1>org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
i1>org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源码简单解析
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
/**
* 转换对应的beanName,或许大家有疑问,传进来的name 不就是beanName么?
* 传进来的有可能是别名,也有可能是FactoryBean
* 1)首先去除factoryBean的修饰符,比如name="&instA" -----> instA
* 2)取指定的alias所表示的最终的beanName,比如传入的是别名ia指向为instA的bean,那么返回的是instA
**/
final String beanName = transformedBeanName(name);
Object bean;
/**
* 设计的精髓
* 检查实例缓存中对象工厂缓存中是否包含对象(从这里返回的可能是实例化好的,也有可能没有实例化好的)
* 为什么要有这段代码?
* 因为单实例bean实例化可能存在循环依赖问题,而为了解决循环依赖问题,在对象刚刚创建好(还没有属性赋值)
* 的时候,就会把对象包装成一个对象工厂暴露出去(加入对象工厂缓存中),一旦下一个bean需要依赖它,就直接从缓存中获取
**/
// 直接从缓存中去取或从对象工厂缓存去取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
/**
* 若从缓存中获取的sharedInstance是原始的bean(属性还没有进行实例化,那么在这里进行处理)
**/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
/**
* 为什么Spring对原型对象无法解决循环依赖问题?
* 因为Spring ioc容器并不会缓存原型对象,所以无法提前暴露对象,每次调用都会创建新的对象
*
* 比如在对象A存在属性对象B,对象B存在属性对象A,在创建A的时候,检查依赖对象B,就会创建对象B
* 创建B的时候又发现依赖对象A,由于是原型对象,ioc容器不会对原型对象进行缓存,所以无法解决循环依赖问题
**/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 获取父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
// 如果beanDefinitionMap以及所有加载的bean不包含 本次加载的bean则从父容器中去加载
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// 从父容器中递归查询
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// 这里不是做类型检查,而是创建bean,这里需要标记一下
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
/**
* 合并父BeanDefinition和子BeanDefinition,后面会单独分析这个方法
**/
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 用来处理bean的加载顺序,比如要在创建instA的情况下要先创建instrB
/**
* <bean id="beanA" class="beanA" depends-on="beanB">
* <bean id="beanB" class="beanA" depends-on="beanA">
* 创建A之前需要创建B,创建B之前需要创建A就会抛出异常
**/
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注册依赖
registerDependentBean(dep, beanName);
try {
// 有限创建依赖对象
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 创建bean,单例
if (mbd.isSingleton()) {
// 创建单例bean
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
// 在getSingleton方法中进行回调的
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 创建非单例的bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
根据上诉的代码中,其实如果看过我之前篇章有介绍spring IOC容器的介绍这里看到这里的代码大家肯定就比较容易理解,在上面的代码中,他第一步:Object sharedInstance = getSingleton(beanName);去缓存中获取sharedInstance对象,这里获取得到的对象,有可能是完全实例化好的,也有可能是一个早期对象。
第二步由于在缓存中获取不到,所以sharedInstance就为空,所以叫调用了对应的createBean去创建对象,然后把创建的对象放入缓存中,并且把早期对象从缓存中删除出去。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 放入到缓存对象中
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
// 暴露对象缓存中移除
this.singletonFactories.remove(beanName);
// 早期对象缓存中移除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 调用。
// 真正创建bean的逻辑
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 调用构造方法来创建bean实例
if (instanceWrapper == null) {
/**
* 如果存在工厂方法则使用工厂方法来进行实例化
* 一个类有多个构造函数,每个构造函数的参数都不同,所以需要根据入参来锁定构造函数然后进行初始化
* 如果既不存在工厂方法又不存在带有参数的构造方法,则使用默认的构造函数进行初始化
**/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
/**
* bean的后置处理器
* bean合并后的处理,
**/
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// 判断当前bean是否需要暴露到缓存对象中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 暴露早期对象到缓存中来解决循环依赖
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 为当前的bean填充属性,发现依赖等解决循环依赖就是在这个地方
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 调用bean的后置处理器以及InitializingBean和自己自定义的方法进行初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 去缓存中获取对象,只有没有发生循环依赖的时候,earlySingletonReference才会为空
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 检查当前的bean在初始化方法中有没有被增强过(代理过)
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
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.");
}
}
}
}
// Register bean as disposable.
try {
// 注册DisposableBean。如果配置了destroy-method,这里注册需要以便在销毁时调用
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
可以看到在上面创建bean的代码中,有这么一句判断earlySingletonExposure 用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。对于单例 bean,该变量一般为 true。
// 提前暴露对象
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 把bean作为ObjectFactory提前暴露出来
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
三、spring解决循环依赖为什么需要三级缓存
其实看到这里小伙伴就会有一个疑问了,spring解决循环依赖就是在创建对象未进行实例化的时候,如果判断这个对象可以提前暴露出去就可以解决循环依赖了,然后到时候需要这个对象的先去对应的缓存里面判断存在不存在就可以了,细心的小伙伴发现好像二级缓存就已经可以解决掉这个循环依赖问题了,为啥spring还要设计三级缓存呢?
我们知道singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法。再来看看循环依赖中是怎么拿其它半成品的实例对象的。
我们假设现在有这样的场景AService依赖BService,BService依赖AService。
他的生命周期如下:
- AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中。
- 填充属性BService,发现BService还未进行过加载,就会先去加载BService。
- 再加载BService的过程中,实例化,也通过ObjectFactory半成品暴露在三级缓存。
-
填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory
拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题???
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景: 我们发现AService的testAopProxy被AOP代理了,看看传入的匿名内部类的getEarlyBeanReference返回的是什么对象。
发现singletonFactory.getObject()返回的是一个AService的代理对象,还是被CGLIB代理的。再看一张再执行一遍singletonFactory.getObject()返回的是否是同一个AService的代理对象
我们会发现再执行一遍singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。还有一个注意的点:
既然singleFactory.getObject()返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过CGLIB代理的AService对象。所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
四、总结
在这一个篇章我们简单的重新复习了一篇Spring Bean的生命周期,也说明了spring是如何解决循环依赖,如果大家没有看过我之前的博客可能对于现在说的这些还是比较难理解的,建议大家先去看下之前对于spring源码的解析:https://blog.csdn.net/jokemqc/category_11387985.html,spring源码篇到这里我们已经学习了Spring IOC、AOP的源码分析了,接下来就是对于spring 事务源码的分析了,但是小伙伴们有没有发现,在上一个篇章中我们介绍了spring aop代码对象是如何调用目标方法的,有介绍到过责任链的设计模式,其实spring源码中使用了大量的设计模式,所以小弟同时也在学习着设计模式,同时也会跟进更新对于设计模式的笔记发布https://blog.csdn.net/jokemqc/category_7272273.html,大家有兴趣的可以跟着博主一起学习,让大家一起进步。