Spring是如何解决循环依赖
- 前言
- 源码解读
- 解析切面类
- 进入循环依赖模块
前言
本篇debug源码的方式去进行。
准备测试代码
切面类:
@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)
解析切面类
/**
* 后置处理器的【第一次】调用 总共有九处调用 事务在这里不会被调用,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;
}
进入循环依赖模块
在第一次创建A的时候会在
加入到singletonsCurrentlyInCreation
里,把A标记为正在创建
回调createBean方法
调用doCreateBean
缓存单例到三级缓存,防止循环依赖。三级缓存存的函数式接口,用于后置处理器调用AOP,代码解耦
在A属性赋值,注入属性B,解析autowired注解
发现了A的autowireB,去getBean(B)。继续createBean(B),可以发现此时循环依赖标识中已经把B加入了
此时属性赋值B,此时正在创建的集合里有了A和B
又发现了A
继续getBean(A)
@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;
}
因为之前A和B已经存到3级缓存里了,此时singletonFactory.getObject()
会调用后置处理器给B中的A创建动态代理
利用完3级缓存中的后置处理器创建了A的动态代理
放到了2级缓存中,从3级缓存中删除了A,
此时B中的A赋值完成。
B的属性赋值完成,
此时什么也拿不到,因为1级缓存里没有,二级缓存里也没有B。移除正在创建的B
把B加入1级缓存里
这时A里的B走完了,实例化,属性赋值,初始化,放入缓存的过程。回到了A的属性赋值
A属性赋值完,去getSingleton,一级缓存没有,并且是正在创建,从2级缓存中拿。
拿到返回,加入1级缓存里。
此时完成了循环依赖的解决