如果你曾经看过Spring解决循环依赖的博客,应该知道它其中有好几个Map,一个Map放的是最完整的对象,称为singletonObjects,一个Map放的是提前暴露出来的对象,称为earlySingletonObjects。
在这里,先要解释下这两个东西:
singletonObjects:单例池,其中存放的是经历了Spring完整生命周期的bean,这里面的bean的依赖都已经填充完毕了。
earlySingletonObjects:提前暴露出来的对象的map,其中存放的是刚刚创建出来的对象,没有经历Spring完整生命周期的bean,这里面的bean的依赖还未填充完毕。
我们可以这么做:
当我们创建完beanA,就把自己放到earlySingletonObjects,发现自己需要beanB,然后就去屁颠屁颠创建beanB;
当我们创建完beanB,就把自己放到earlySingletonObjects,发现自己需要beanA,然后就去屁颠屁颠创建beanA;
创建beanA前,先去earlySingletonObjects看一下,发现自己已经被创建出来了,把自己返回出去;
beanB拿到了beanA,beanB创建完毕,把自己放入singletonObjects;
beanA可以去singletonObjects拿到beanB了,beanA也创建完毕,把自己放到singletonObjects。
整个过程结束。
下面让我们来实现这个功能:
首先,自定义一个注解,字段上打上这个注解的,说明需要被Autowired:
再创建两个循环依赖的类:
public class OrderService { @CodeBearAutowired public UserService userService; } public class UserService { @CodeBearAutowired public OrderService orderService; }然后就是核心,创建对象,填充属性,并解决Spring循环依赖的问题:
public class Cycle { // 单例池,里面放的是完整的bean,已完成填充属性 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 存放的是提前暴露出来的bean,没有经历过spring完整的生命周期,没有填充属性 private final Map<String, Object> earlySingletonObjects = new HashMap<>(); // 在Spring中,这个map存放的是beanNam和beanDefinition的映射关系 static Map<String, Class<?>> map = new HashMap<>(); static { map.put("orderService", OrderService.class); map.put("userService", UserService.class); } // 如果先调用init方法,就是预加载,如果直接调用getBean就是懒加载,两者的循环依赖问题都解决了 public void init() { for (Map.Entry<String, Class<?>> stringClassEntry : map.entrySet()) { createBean(stringClassEntry.getKey()); } } public Object getBean(String beanName) { // 尝试从singletonObjects中取, Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject != null) { return singletonObject; } // 尝试从earlySingletonObjects取 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject != null) { return singletonObject; } return createBean(beanName); } private Object createBean(String beanName) { Object singletonObject; try { // 创建对象 singletonObject = map.get(beanName).getConstructor().newInstance(); // 把没有完成填充属性的半成品 bean 放入earlySingletonObjects earlySingletonObjects.put(beanName, singletonObject); // 填充属性 populateBean(singletonObject); // bean创建成功,放入singletonObjects this.singletonObjects.put(beanName, singletonObject); return singletonObject; } catch (Exception ignore) { } return null; } private void populateBean(Object object) { Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { if (field.getAnnotation(CodeBearAutowired.class) != null) { Object value = getBean(field.getName()); try { field.setAccessible(true); field.set(object, value); } catch (IllegalAccessException ignored) { } } } } }预加载调用:
public class Main { public static void main(String[] args) { Cycle cycle = new Cycle(); cycle.init(); UserService userService = (UserService) cycle.getBean("userService"); OrderService orderService = (OrderService) cycle.getBean("orderService"); System.out.println(userService.orderService); System.out.println(orderService.userService); } }运行结果:
com.codebear.cycleeasy.OrderService@61baa894 com.codebear.cycleeasy.UserService@b065c63懒加载调用:
public class Main { public static void main(String[] args) { Cycle cycle = new Cycle(); UserService userService = (UserService) cycle.getBean("userService"); OrderService orderService = (OrderService) cycle.getBean("orderService"); System.out.println(userService.orderService); System.out.println(orderService.userService); } }运行结果:
com.codebear.cycleeasy.OrderService@61baa894 com.codebear.cycleeasy.UserService@b065c63 为什么无法解决原型、构造方法注入的循环依赖在上面,我们自己手写了解决循环依赖的代码,可以看到,核心是利用一个map,来解决这个问题的,这个map就相当于缓存。