循环依赖的学习
导言
在gitee上搜索了一下,循环依赖的解决方式,正好看到有人手写了一个循环依赖,就顺便学习了一下。记录如下。
地址:https://gitee.com/jackdawl/spring-circledependency.git
什么是循环依赖
循环依赖就是在a的类中注入b,在b的类中注入a.此时spring先读取a,然后读取a中的b属性。在b中又发现了a的属性。此时就会进入无限循环
项目结构
前置准备
pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jackdawl</groupId> <artifactId>spring-circledependency</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> </dependencies> </project>
定义Iservice
package com.jackdawl; public interface IService { void test(); }
定义OrderService
package com.jackdawl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class OrderService { @Autowired private IService userService; public OrderService(){ System.out.println("实例化 OrderService"); } }
定义UserService
package com.jackdawl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class UserService implements IService{ @Autowired private OrderService orderService; public UserService(){ System.out.println("实例化 UserService"); } @Override public void test() { System.out.println("实现类:UserService"); } }
循环依赖的测试
package com.jackdawl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 模拟循环依赖 */ public class MainClass1 { public static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); //一级缓存 public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //模拟注册BeanDefinition public static void loadBeanDefinitionMap() { BeanDefinition bd1 = new RootBeanDefinition(UserService.class); BeanDefinition bd2 = new RootBeanDefinition(OrderService.class); beanDefinitionMap.put("userService", bd1); beanDefinitionMap.put("orderService", bd2); } public static void main(String[] args) { loadBeanDefinitionMap(); beanDefinitionMap.forEach((beanName, beanDefinition) -> { try { Object bean = getBean(beanName); } catch (Exception e) { e.printStackTrace(); } }); } public static Object getBean(String beanName) throws Exception { //创建bean RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); Object instanceBean = beanClass.newInstance(); //填充属性 Field[] declaredFields = beanClass.getDeclaredFields(); for (Field f : declaredFields) { Autowired autowired = f.getAnnotation(Autowired.class); if (autowired != null) { //从Spring 获取依赖对象 Object object = getBean(f.getName()); f.set(instanceBean, object); } } //初始化 略 //添加到单例池 singletonObjects.put(beanName, instanceBean); return instanceBean; } }
执行代码
发现一直在交替打印,符合上面讲的无限循环,直到堆栈异常
那么怎么解决呢?
/** * 解决循环依赖 * 实例化后就加入到单例池 * getBean 先从单例池拿,没有在去创建 */
解决循环依赖的第一种方式
package com.jackdawl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 解决循环依赖 * 实例化后就加入到单例池 * getBean 先从单例池拿,没有在去创建 */ public class MainClass2 { public static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); //一级缓存 public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //模拟注册BeanDefinition public static void loadBeanDefinitionMap() { BeanDefinition bd1 = new RootBeanDefinition(UserService.class); BeanDefinition bd2 = new RootBeanDefinition(OrderService.class); beanDefinitionMap.put("userService", bd1); beanDefinitionMap.put("orderService", bd2); } public static void main(String[] args) { loadBeanDefinitionMap(); beanDefinitionMap.forEach((beanName, beanDefinition) -> { try { Object bean = getBean(beanName); } catch (Exception e) { e.printStackTrace(); } }); } public static Object getBean(String beanName) throws Exception { Object instanceBean = getSingleton(beanName); if (instanceBean != null){ return instanceBean; } //创建bean RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); instanceBean = beanClass.newInstance(); //添加到单例池 singletonObjects.put(beanName, instanceBean); //填充属性 Field[] declaredFields = beanClass.getDeclaredFields(); for (Field f : declaredFields) { Autowired autowired = f.getAnnotation(Autowired.class); if (autowired != null) { //禁用安全检查,私有属性,保证可见;不禁用会报错 f.setAccessible(true); //从Spring 获取依赖对象 Object object = getBean(f.getName()); f.set(instanceBean, object); } } //初始化 略 return instanceBean; } public static Object getSingleton(String beanName){ if (singletonObjects.containsKey(beanName)){ return singletonObjects.get(beanName); } return null; } }
执行结果
看看是怎么执行的
首先进入的是userService,如果不存在就会添加进入到一级缓存中
然后读取属性发现到了orderservice,获取依赖对象,需要循环获得
orderservice从一级缓存中也是获取不到的,所以也是走添加到单例池的操作。然后在它的属性中也发现了userService,继续循环
此时从一级缓存中是可以获得userService的。直接返回。
返回的过程中发现当前的类还是orderservice的。所以还是先实例化orderservice里面的userservice,然后再实例化orderservice
orderservice实例化完成就会重新进入回到userservice中,最终实例化userservice
执行流程为
userservice->userservice.orderservice->orderservice.userservice->orderservice->userservice
spring是怎么实现这个功能的呢?
定义JdkDynamicProxy
package com.jackdawl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkDynamicProxy implements InvocationHandler { private Object target; public JdkDynamicProxy(Object target) { this.target = target; } public <T> T getProxy() { return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入动态代理 invoke 方法"); return method.invoke(target,args); } }
定义MyProxyBeanPostProcessor
package com.jackdawl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; public class MyProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { // 模拟 UserService 需要创建代理 if(bean instanceof UserService) { JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(bean); return jdkDynamicProxy.getProxy(); } //不需要创建代理的直接返回原始bean return bean; } }
test
package com.jackdawl; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import java.lang.management.BufferPoolMXBean; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * 解决循环依赖 * 实例化后就加入到单例池 * getBean 先从单例池拿,没有在去创建 * 如果 userService 需要创建动态代理,最后放入单例池中的对象是代理对象,但是在 orderService 注入的对象是原始对象,这就产生了冲突。 * 所以要标记发生循环依赖时,orderService 能拿到 userService 的原始对象或者原始对象的代理 * 具体拿到哪种对象的逻辑封装到一个函数接口中,添加到三级缓存 * 正在创建的bean的 beanName 都放到一个集合中,集合中能查到表明发生循环依赖, * 此时去缓存中拿对象,一二级都没有,就由三级缓存函数接口返回对象放入二级缓存, * 便于多个对象依赖 userService 时,单例池拿不到直接从二级缓存获取 * 如果 userService 需要AOP创建代理,需要判断 */ public class MainClass3 { public static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); //一级缓存 public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //二级缓存 public static Map<String, Object> earlySingletonObjects = new HashMap<>(16); //三级缓存 public static Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); //创建过代理对象的原始对象 beanName-bean private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16); //正在创建的bean的bean名称 public static Set<String> singletonCurrentlyInCreation = new HashSet<>(); //模拟注册BeanDefinition public static void loadBeanDefinitionMap() { BeanDefinition bd1 = new RootBeanDefinition(UserService.class); BeanDefinition bd2 = new RootBeanDefinition(OrderService.class); beanDefinitionMap.put("userService", bd1); beanDefinitionMap.put("orderService", bd2); } public static void main(String[] args) { loadBeanDefinitionMap(); beanDefinitionMap.forEach((beanName, beanDefinition) -> { try { Object bean = getBean(beanName); } catch (Exception e) { e.printStackTrace(); } }); } public static Object getBean(String beanName) throws Exception { Object instanceBean; instanceBean = getSingleton(beanName); if (instanceBean != null) { return instanceBean; } //创建bean RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); instanceBean = beanClass.newInstance(); final Object bean = instanceBean; singletonCurrentlyInCreation.add(beanName); singletonFactories.put(beanName, () -> new MyProxyBeanPostProcessor().getEarlyBeanReference(bean, beanName)); //填充属性 Field[] declaredFields = beanClass.getDeclaredFields(); for (Field f : declaredFields) { Autowired autowired = f.getAnnotation(Autowired.class); if (autowired != null) { //禁用安全检查,私有属性,保证可见;不禁用会报错 f.setAccessible(true); //从Spring 获取依赖对象 Object object = getBean(f.getName()); f.set(instanceBean, object); } } //初始化 略 //模拟需要AOP则从二级缓存拿代理对象 if (earlySingletonObjects.containsKey(beanName)){ instanceBean = earlySingletonObjects.get(beanName); singletonObjects.put(beanName, instanceBean); }else { singletonObjects.put(beanName, instanceBean); } //删除二三级缓存,删除创建中集合beanName singletonFactories.remove(beanName); earlySingletonObjects.remove(beanName); singletonCurrentlyInCreation.remove(beanName); return instanceBean; } public static Object getSingleton(String beanName) { //c从单例池拿 Object instanceBean = singletonObjects.get(beanName); //单例池没拿到对象,而且发生循环依赖就从二级缓存拿 if (instanceBean == null && singletonCurrentlyInCreation.contains(beanName)) { instanceBean = earlySingletonObjects.get(beanName); if (instanceBean == null) { //二级缓存拿不到对象就从三级缓存拿 ObjectFactory<?> objectFactory = singletonFactories.get(beanName); if (objectFactory != null) { instanceBean = objectFactory.getObject(); earlySingletonObjects.put(beanName, instanceBean); singletonFactories.remove(beanName); } } } return instanceBean; } }
打断点看看
首先是userService
单例池为空,正在创建的bean名称为空,直接返回null
加入正在创建的bean,加入三级缓存中。读取属性orderservice
进入orderservice
orderservice也是直接返回
又会进入userservice
此时一级缓存没有,正在创建的bean有,从二级缓存中拿,二级缓存没有,从三级缓存中拿,拿出来,加入二级缓存,删除三级缓存
然后直接返回userservice
接着回到orderservice
二级缓存中没有orderservice,直接加入一级缓存中,然后删除三级缓存,二级缓存以及正在创建的dean
测试结果
本文来自博客园,作者:小陈子博客,转载请注明原文链接:https://www.cnblogs.com/cj8357475/p/16418983.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)