Spring是如何解决循环依赖

  • 前言
  • 源码解读
  • 解析切面类
  • 进入循环依赖模块

前言

本篇debug源码的方式去进行。

准备测试代码

spring解决循环依赖的问题_赋值

切面类:

@Aspect
@Order
@Component
public class TulingLogAspect {

	public TulingLogAspect() {
		System.out.println("加载TulingLog");
	}

	@Pointcut("execution(* tuling.TulingCalculateA.*(..))")
    public void pointCut(){};

    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法【"+methodName+"】的<前置通知>,入参"+ Arrays.asList(joinPoint.getArgs()));
    }

    @After(value = "pointCut()")
    public void methodAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法【"+methodName+"】的<后置通知>,入参"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void methodReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法【"+methodName+"】的<返回通知>,入参"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterThrowing(value = "pointCut()")
    public void methodAfterThrowing(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法【"+methodName+"】的<异常通知>,入参"+Arrays.asList(joinPoint.getArgs()));
    }

}
@Component
public class TulingCalculateA {

	@Autowired
	TulingCalculateB tulingCalculateB;

    public int add(int numA, int numB) {

        System.out.println("执行目标方法:add");
        return numA+numB;
    }

    public int sub(int numA, int numB) {
        System.out.println("执行目标方法:reduce");
        return numA-numB;
    }

    public int div(int numA, int numB) {
        return tulingCalculateB.div(numA,numB);
    }
//
    public int multi(int numA, int numB) {
        return tulingCalculateB.multi(numA,numB);
    }
//
    public int mod(int numA,int numB){
        System.out.println("执行目标方法:mod");

		int retVal = ((Calculate)AopContext.currentProxy()).add(numA,numB);
        //int retVal = this.add(numA,numB);

        return retVal%numA;

        //return numA%numB;
    }

}
@Component
public class TulingCalculateB {//implements Calculate{

	@Autowired
	TulingCalculateA tulingCalculateA;


	public int add(int numA, int numB) {
		return tulingCalculateA.add(numA,numB);
	}

	public int sub(int numA, int numB) {
		return tulingCalculateA.sub(numA,numB);
	}

	public int div(int numA, int numB) {
		System.out.println("执行目标方法:div");

		return numA/numB;
	}
    public int multi(int numA, int numB) {
        System.out.println("执行目标方法:multi");

        return numA*numB;
    }

    public int mod(int numA,int numB){
        System.out.println("执行目标方法:mod");

		int retVal = ((Calculate)AopContext.currentProxy()).add(numA,numB);
        //int retVal = this.add(numA,numB);

        return retVal%numA;

        //return numA%numB;
    }

}

配置类

@Configuration
@EnableAspectJAutoProxy  /*<aop:aspectj-autoproxy/>*/
//(exposeProxy = true) //(proxyTargetClass = true)
@ComponentScan("tuling")
public class MainConfig {


}

源码解读

解析配置类,生成bean定义的我们就不看了,之前分析过,之间来到finishBeanFactoryInitialization(实例化我们剩余的单实例bean)

spring解决循环依赖的问题_spring解决循环依赖的问题_02


spring解决循环依赖的问题_缓存_03


spring解决循环依赖的问题_赋值_04

解析切面类

spring解决循环依赖的问题_赋值_05

/**
* 后置处理器的【第一次】调用 总共有九处调用 事务在这里不会被调用,aop的才会被调用
* 为啥aop在这里调用了,因为在此处需要解析出对应的切面保存到缓存中
*/
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
	/**
	 * 获取容器中的所有后置处理器
	 */
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
		//判断后置处理器是不是InstantiationAwareBeanPostProcessor
		if (bp instanceof InstantiationAwareBeanPostProcessor) {//是不是实现了InstantiationAwareBeanPostProcessor接口
			//把我们的BeanPostProcessor强制转为InstantiationAwareBeanPostProcessor
			InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
			/**
			 * 【很重要】
			 * 我们AOP @EnableAspectJAutoProxy 为我们容器中导入了 AnnotationAwareAspectJAutoProxyCreator
			 * 我们事务注解@EnableTransactionManagement 为我们的容器导入了 InfrastructureAdvisorAutoProxyCreator
			 * 都是实现了我们的 BeanPostProcessor接口,InstantiationAwareBeanPostProcessor,
			 * 进行后置处理解析切面
			 */
			Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
			if (result != null) {
				return result;
			}
		}
	}
	return null;
}

spring解决循环依赖的问题_spring解决循环依赖的问题_06

进入循环依赖模块

spring解决循环依赖的问题_缓存_07


spring解决循环依赖的问题_赋值_08

在第一次创建A的时候会在

spring解决循环依赖的问题_spring解决循环依赖的问题_09


加入到singletonsCurrentlyInCreation里,把A标记为正在创建

spring解决循环依赖的问题_spring解决循环依赖的问题_10


回调createBean方法

spring解决循环依赖的问题_System_11


调用doCreateBean

spring解决循环依赖的问题_赋值_12

缓存单例到三级缓存,防止循环依赖。三级缓存存的函数式接口,用于后置处理器调用AOP,代码解耦

spring解决循环依赖的问题_赋值_13

在A属性赋值,注入属性B,解析autowired注解

spring解决循环依赖的问题_System_14

spring解决循环依赖的问题_System_15

spring解决循环依赖的问题_缓存_16


发现了A的autowireB,去getBean(B)。继续createBean(B),可以发现此时循环依赖标识中已经把B加入了

spring解决循环依赖的问题_赋值_17

spring解决循环依赖的问题_缓存_18


此时属性赋值B,此时正在创建的集合里有了A和B

spring解决循环依赖的问题_spring解决循环依赖的问题_19


spring解决循环依赖的问题_缓存_20


又发现了A

spring解决循环依赖的问题_赋值_21


继续getBean(A)

spring解决循环依赖的问题_赋值_22


spring解决循环依赖的问题_赋值_23

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	/**
	 * 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
	 * IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
	 */

	Object singletonObject = this.singletonObjects.get(beanName);
	/**
	 * 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
	 * IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
	 */
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			/**
			 * 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
			 * 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态)
			 * 就是早期对象
			 */
			singletonObject = this.earlySingletonObjects.get(beanName);
			/**
			 * 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
			 */
			if (singletonObject == null && allowEarlyReference) {
				/**
				 * 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
				 * 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
				 * 暴露到三级缓存中
				 */
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				//从三级缓存中获取到对象不为空
				if (singletonFactory != null) {
					/**
					 * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
					 * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
					 */
					singletonObject = singletonFactory.getObject();
					//把早期对象放置在二级缓存,
					this.earlySingletonObjects.put(beanName, singletonObject);
					//ObjectFactory 包装对象从三级缓存中删除掉
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

spring解决循环依赖的问题_缓存_24


因为之前A和B已经存到3级缓存里了,此时singletonFactory.getObject()

会调用后置处理器给B中的A创建动态代理

spring解决循环依赖的问题_spring解决循环依赖的问题_25


spring解决循环依赖的问题_System_26


spring解决循环依赖的问题_spring解决循环依赖的问题_27


利用完3级缓存中的后置处理器创建了A的动态代理

spring解决循环依赖的问题_缓存_28


放到了2级缓存中,从3级缓存中删除了A,

spring解决循环依赖的问题_赋值_29


此时B中的A赋值完成。

spring解决循环依赖的问题_System_30


B的属性赋值完成,

spring解决循环依赖的问题_spring解决循环依赖的问题_31


spring解决循环依赖的问题_spring解决循环依赖的问题_32


此时什么也拿不到,因为1级缓存里没有,二级缓存里也没有B。移除正在创建的B

spring解决循环依赖的问题_System_33


把B加入1级缓存里

spring解决循环依赖的问题_spring解决循环依赖的问题_34


这时A里的B走完了,实例化,属性赋值,初始化,放入缓存的过程。回到了A的属性赋值

spring解决循环依赖的问题_缓存_35


A属性赋值完,去getSingleton,一级缓存没有,并且是正在创建,从2级缓存中拿。

spring解决循环依赖的问题_缓存_36

拿到返回,加入1级缓存里。

spring解决循环依赖的问题_赋值_37


此时完成了循环依赖的解决

spring解决循环依赖的问题_缓存_38