Spring循环依赖源码调试详解,用两级缓存代替三级缓存
Spring循环依赖源码详解,改用两级缓存并实验
背景
最近一直在研究Spring的循环依赖,发现好像两级缓存也能解决循环依赖。
关于为何使用三级缓存,大致有两个原因
- 对于AOP的类型,保证Bean生命周期的顺序
对于有AOP代理增强的类型,如果没有循环依赖,那么AOP的增强逻辑的执行点在:
无循环依赖:
Container->>Bean: 1. 实例化(constructor)Container->>Bean: 2. 属性注入(populate)Container->>Processor: 3. 调用postProcessAfterInitialization()Processor->>Processor: 4. 创建代理(wrapIfNecessary)Processor-->>Container: 5. 返回代理对象
在第4步,也就是初始化后置处理器postProcessAfterInitialization
实际代理包装在BeanPostProcessor子类AbstractAutoProxyCreator类中:
public Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean); // 标记为提前代理return wrapIfNecessary(bean, beanName, cacheKey); // 创建代理
}
有循环依赖
Container->>Bean: 1. 实例化(半成品)Container->>EarlyCache: 2. 存入singletonFactoriesContainer->>Bean: 3. 属性注入(触发循环依赖)Container->>EarlyCache: 4. 获取早期引用 → getEarlyBeanReference()EarlyCache->>Processor: 5. 调用getEarlyBeanReference()Processor->>Processor: 6. 创建代理(提前)Processor-->>Container: 7. 返回代理对象Container->>Bean: 8. 继续属性注入和初始化Container->>Processor: 9. postProcessAfterInitialization() Processor-->>Container: 10. 返回同一个代理(已存在则不重复创建)
这里是在循环依赖注入的过程中发生的,提前了
其实在哪里进行代理并无实际影响,因为不会影响类实例的成员
2、第二个原因
是在实例化后依赖注入之前,会把这个ObjectFactory的对象放到三级缓存,延迟创建代理实例,后续有循环依赖,回到三级缓存拿到这个,并调用ObjectFactory.getObject方法进行真正的创建,多次调用会产生多个实例,这里可以及时创建实例,不必等到延迟加载,就解决了
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
改用两级缓存
(针对单例循环setter场景,修改spring源码,三级缓存改为两级缓存)
/*** 修改:20250819 11:57 直接加入二级缓存 不用三级缓存 看一下能不能解决循环依赖* @param beanName* @param singletonFactory*/protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//this.singletonFactories.put(beanName, singletonFactory); // 加入工厂 暴露给外部this.earlySingletonObjects.put(beanName,singletonFactory.getObject());// 确保二级缓存不会存在相同的beanthis.registeredSingletons.add(beanName);}}}
这里直接把三级缓存注释,在实例化完成后直接生成代理对象
创建测试类
@Aspect // 切面类
@Component
public class LogAspect {// 切入点表达式:匹配所有 MyServiceImpl 下的方法@Pointcut("execution(* com.jdkProxy.MyServiceImpl.*(..))")public void userServiceMethods() {// 方法体必须为空,不能写任何逻辑!}// 前置通知:方法执行前@Before("userServiceMethods()")public void beforeMethod(JoinPoint joinPoint) {System.out.println("📌 Before method: " + joinPoint.getSignature().getName());}// 后置通知:方法正常返回后@AfterReturning(pointcut = "userServiceMethods()", returning = "result")public void afterReturning(JoinPoint joinPoint, Object result) {System.out.println("✅ Method returned: " + joinPoint.getSignature().getName());}// 异常通知:方法抛出异常后@AfterThrowing(pointcut = "userServiceMethods()", throwing = "ex")public void afterThrowing(JoinPoint joinPoint, Exception ex) {System.out.println("💥 Exception in method: " + joinPoint.getSignature().getName() + ", ex: " + ex);}// 环绕通知:可以控制整个方法执行@Around("userServiceMethods()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("🔄 Around before: " + pjp.getSignature().getName());Object result = pjp.proceed(); // 执行目标方法System.out.println("🔄 Around after: " + pjp.getSignature().getName());return result;}
}
@Component
public class MyServiceImpl implements MyService{@AutowiredMyServiceImpl2 myServiceImpl2;public MyServiceImpl2 getMyServiceImpl2() {return myServiceImpl2;}public void setMyServiceImpl2(MyServiceImpl2 myServiceImpl2) {this.myServiceImpl2 = myServiceImpl2;}public void eat(){System.out.println("吃饭服务");}@Overridepublic void mainMethod() {eat();}
}
@Component
public class MyServiceImpl2 {@AutowiredMyServiceImpl myService;public MyServiceImpl getMyService() {return myService;}public void setMyService(MyServiceImpl myService) {this.myService = myService;}
}
@Component
public class MyServiceImpl3 {@AutowiredMyServiceImpl myService;public MyServiceImpl getMyService() {return myService;}public void setMyService(MyServiceImpl myService) {this.myService = myService;}
}
MyServiceImpl ->myServiceImpl2
MyServiceImpl2 -> MyServiceImpl
MyServiceImpl3 ->MyServiceImpl
启动容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.scan("com"); // 扫描包中的注解 进行BeanDefinintion 注册context.refresh();MyServiceImpl m1 = context.getBean(MyServiceImpl.class);System.out.println("m1->m2:"+m1.getMyServiceImpl2());MyServiceImpl2 m2 = context.getBean(MyServiceImpl2.class);MyServiceImpl3 m3 = context.getBean(MyServiceImpl3.class);System.out.println("m1:"+m1);System.out.println("m2->m1:"+m2.getMyService());m2.getMyService().eat();System.out.println("m3->m1:"+m3.getMyService());m3.getMyService().eat();
输出结果:
m1:com.jdkProxy.MyServiceImpl@29c2c826
m2:com.jdkProxy.MyServiceImpl2@253b380a
m3:com.jdkProxy.MyServiceImpl3@6818d900
------------------------------------------
🔄 Around before: getMyServiceImpl2
📌 Before method: getMyServiceImpl2
✅ Method returned: getMyServiceImpl2
🔄 Around after: getMyServiceImpl2
m1->m2:com.jdkProxy.MyServiceImpl2@253b380a
m2->m1:com.jdkProxy.MyServiceImpl@29c2c826
m3->m1:com.jdkProxy.MyServiceImpl@29c2c826
------------------------------------------
🔄 Around before: eat
📌 Before method: eat
吃饭服务
✅ Method returned: eat
🔄 Around after: eat
------------------------------------------
🔄 Around before: eat
📌 Before method: eat
吃饭服务
✅ Method returned: eat
🔄 Around after: eat
🔄 Around before: eat
📌 Before method: eat
吃饭服务
✅ Method returned: eat
🔄 Around after: eat
🔄 Around before: mainMethod
📌 Before method: mainMethod
吃饭服务
✅ Method returned: mainMethod
🔄 Around after: mainMethod