spring 循环依赖
spring 多例下循环依赖报错:(不能使用多例进行循环依赖)
代码如下:
@Component @Scope("prototype") public class A { @Autowired private B b; } @Component @Scope("prototype") public class B { @Autowired private C c; } @Component @Scope("prototype") public class C { @Autowired private A a; }
Description: The dependencies of some of the beans in the application context form a cycle: userController (field private com.xiang.si.bean.A com.xiang.si.controller.UserController.a) ┌─────┐ | a (field private com.xiang.si.bean.B com.xiang.si.bean.A.b) ↑ ↓ | b (field private com.xiang.si.bean.C com.xiang.si.bean.B.c) ↑ ↓ | c (field private com.xiang.si.bean.A com.xiang.si.bean.C.a) └─────┘
spring 解决循环依赖(必须是单例)
spring内部维护了三个Map, 也就是我们通常说的三级缓存。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 俗称 "单例池" "容器" 用于缓存创建的单例 private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); // 映射创建bean的原始工厂 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); // 映射bean早期的引用,也就是这个map里面的bean不是完整的,甚至还不能称之为bean,只是一个instance private final Map<String, Object> earlySingletonObjects = new HashMap(16); // 缓存正在创建的bean private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16)); }
循环依赖的本质
- 指定的一些类实例为单例
- 类中的字段实例也为单例
- 支持循环依赖
手写循环依赖:
A类:
public class A { private B b; public B getB() { return b; } }
B类:
public class A { private B b; public B getB() { return b; } }
test:
public class Test01 { /** * 放置创建好的bean Map */ private static Map<String, Object> cacheMap = new HashMap<>(2); public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { // 假装扫描出来的对象 Class[] classes = {A.class, B.class}; // 假装项目初始化实例化所有bean for (Class aClass : classes) { getBean(aClass); } // check System.out.println(getBean(B.class).getA() == getBean(A.class)); System.out.println(getBean(A.class).getB() == getBean(B.class)); } private static <T> T getBean(Class<T> beanClass) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { // 本文用类名小写 简单代替bean的命名规则 String beanName = beanClass.getSimpleName().toLowerCase(); // 如果已经是一个bean,则直接返回 if (cacheMap.containsKey(beanName)) { return (T) cacheMap.get(beanName); } // 将对象本身实例化 Object object = beanClass.getDeclaredConstructor().newInstance(); // 放入缓存 cacheMap.put(beanName, object); // 把所有字段当成需要注入的bean,创建并注入到当前bean中 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 获取需要注入字段的class Class<?> fieldClass = field.getType(); String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); // 如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可 // 如果缓存没有 继续创建 field.set(object, cacheMap.containsKey(fieldBeanName) ? cacheMap.get(fieldBeanName) : getBean(fieldClass)); } // 属性填充完成,返回 return (T) object; } }