Spring循环依赖源码详解,改用两级缓存并实验
背景
最近一直在研究Spring的循环依赖,发现好像两级缓存也能解决循环依赖。
关于为何使用三级缓存,大致有两个原因
- 对于AOP的类型,保证Bean生命周期的顺序
对于有AOP代理增强的类型,如果没有循环依赖,那么AOP的增强逻辑的执行点在:
无循环依赖:
java
Container->>Bean: 1. 实例化(constructor)
Container->>Bean: 2. 属性注入(populate)
Container->>Processor: 3. 调用postProcessAfterInitialization()
Processor->>Processor: 4. 创建代理(wrapIfNecessary)
Processor-->>Container: 5. 返回代理对象
在第4步,也就是初始化后置处理器postProcessAfterInitialization
实际代理包装在BeanPostProcessor子类AbstractAutoProxyCreator类中:
java
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean); // 标记为提前代理
return wrapIfNecessary(bean, beanName, cacheKey); // 创建代理
}
有循环依赖
java
Container->>Bean: 1. 实例化(半成品)
Container->>EarlyCache: 2. 存入singletonFactories
Container->>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方法进行真正的创建,多次调用会产生多个实例,这里可以及时创建实例,不必等到延迟加载,就解决了
java
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
改用两级缓存
(针对单例循环setter场景,修改spring源码,三级缓存改为两级缓存)
java
/**
* 修改: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());// 确保二级缓存不会存在相同的bean
this.registeredSingletons.add(beanName);
}
}
}
这里直接把三级缓存注释,在实例化完成后直接生成代理对象
创建测试类
java
@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;
}
}
java
@Component
public class MyServiceImpl implements MyService{
@Autowired
MyServiceImpl2 myServiceImpl2;
public MyServiceImpl2 getMyServiceImpl2() {
return myServiceImpl2;
}
public void setMyServiceImpl2(MyServiceImpl2 myServiceImpl2) {
this.myServiceImpl2 = myServiceImpl2;
}
public void eat(){
System.out.println("吃饭服务");
}
@Override
public void mainMethod() {
eat();
}
}
java
@Component
public class MyServiceImpl2 {
@Autowired
MyServiceImpl myService;
public MyServiceImpl getMyService() {
return myService;
}
public void setMyService(MyServiceImpl myService) {
this.myService = myService;
}
}
java
@Component
public class MyServiceImpl3 {
@Autowired
MyServiceImpl myService;
public MyServiceImpl getMyService() {
return myService;
}
public void setMyService(MyServiceImpl myService) {
this.myService = myService;
}
}
java
MyServiceImpl ->myServiceImpl2
MyServiceImpl2 -> MyServiceImpl
MyServiceImpl3 ->MyServiceImpl
启动容器
java
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();
输出结果:
java
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