如何理解Spring Bean IOC、AOP的循环依赖

发布时间:2021-10-12 10:03:55 作者:iii
来源:亿速云 阅读:126

这篇文章主要介绍“如何理解Spring Bean IOC、AOP的循环依赖”,在日常操作中,相信很多人在如何理解Spring Bean IOC、AOP的循环依赖问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解Spring Bean IOC、AOP的循环依赖”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

如何理解Spring Bean IOC、AOP的循环依赖

目录

一、前言

延迟满足能给你带来什么?

大学有四年时间,但几乎所有人都是临近毕业才发现找一份好工作费劲,尤其是我能非常熟悉的软件开发行业,即使是毕业了还需要额外花钱到培训机构,在学一遍编程技术才能出去找工作。好像在校这几年压根就没学到什么!

就我个人而言可能是因为上学期间喜欢编程,也从师哥、师姐那里听到一些关于毕业后找工作的不容易,也了解了一些社会上对程序员开发技能的要求级别。也就是得到了这些消息,又加上自己乐于折腾,我给自己定了一个每天都能完成的小目标:

红尘世界几个王,我自不服迎头上。

日敲代码两百行,冲进世界五百强。

哈哈哈,就这么每天两百行代码,一个月就是6千行,一年就是6万行,三年后开始实习就有18万行,一个应届实习生有将近20万行代码的敲击量,几乎已经可以非常熟练的完成各类简单的工作,在加上实习中对整个项目流程真正的断链后,找一个正经的开发工作,还是很容易的。

而这时候找工作的容易,就来自于你一直以来的学习和沉淀,但如果你没经过这些努力,可能等毕业后就会变得非常慌乱,最后没办法,只能去一些机构再学习一遍。

二、面试题

谢飞机,小记!,以前感觉Spring没啥,看过一篇getBean,我的天!

谢飞机:面试官,最近我看了 Spring 的 getBean 发现这里好多东西,还有一个是要解决循环依赖的,这玩意面试有啥要问的吗?

面试官:有哇,Spring 是如何解决循环依赖的?

谢飞机:嗯,通过三级缓存提前暴露对象解决的。

面试官:可以哈,那这三个缓存里都存放了什么样的对象信息呢?

谢飞机:一级缓存存放的是完整对象,也叫成品对象。二级缓存存放的是半成品对象,就是那些属性还没赋值的对象。三级缓存存放的是 ObjectFactory  类型的 lambda 表达式,就是这用于处理 AOP 循环依赖的。

面试官:可以呀,谢飞机有所准备嘛!那如果没有三级缓存,只有二级或者一级,能解决循环依赖吗?

谢飞机:其实我看过资料了,可以解决,只不过 Spring  要保证几个事情,只有一级缓存处理流程没法拆分,复杂度也会增加,同时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来也更加优雅、简单、易扩展。另外  Spring 的两大特性中不仅有 IOC 还有 AOP,也就是基于字节码增强后的方法,该存放到哪,而三级缓存最主要,要解决的循环依赖就是对 AOP  的处理,但如果把 AOP 代理对象的创建提前,那么二级缓存也一样可以解决。但是,这就违背了 Spring 创建对象的原则,Spring 更喜欢把所有的普通  Bean 都初始化完成,在处理代理对象的初始化。

面试官:飞机,不错嘛,这次了解了不少。那问个简单的,你撸过循环依赖的解决方案?

谢飞机:哦哦,这没有,没实践过!!!确实应该搞一下,试试。

三、什么是循环依赖?

1. 问题描述

了解问题的本质再分析问题,往往更利于对问题有更深入的了解和研究。所以我们在分析 Spring 关于循环依赖的源码之前,先要了解下什么是循环依赖。

如何理解Spring Bean IOC、AOP的循环依赖

循环依赖分为三种,自身依赖于自身、互相循环依赖、多组循环依赖。

但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。

所以 Spring  提供了除了构造函数注入和原型注入外的,setter循环依赖注入解决方案。那么我们也可以先来尝试下这样的依赖,如果是我们自己处理的话该怎么解决。

2. 问题体现

public class ABTest {      public static void main(String[] args) {         new ClazzA();     }  }  class ClazzA {      private ClazzB b = new ClazzB();  }  class ClazzB {      private ClazzA a = new ClazzA();  }

3. 问题处理

在这部分的代码中就一个核心目的,我们来自己解决一下循环依赖,方案如下:

public class CircleTest {      private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);      public static void main(String[] args) throws Exception {         System.out.println(getBean(B.class).getA());         System.out.println(getBean(A.class).getB());     }      private static <T> T getBean(Class<T> beanClass) throws Exception {         String beanName = beanClass.getSimpleName().toLowerCase();         if (singletonObjects.containsKey(beanName)) {             return (T) singletonObjects.get(beanName);         }         // 实例化对象入缓存         Object obj = beanClass.newInstance();         singletonObjects.put(beanName, obj);         // 属性填充补全对象         Field[] fields = obj.getClass().getDeclaredFields();         for (Field field : fields) {             field.setAccessible(true);             Class<?> fieldClass = field.getType();             String fieldBeanName = fieldClass.getSimpleName().toLowerCase();             field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass));             field.setAccessible(false);         }         return (T) obj;     }  }  class A {      private B b;      // ...get/set }  class B {     private A a;    // ...get/set }

如何理解Spring Bean IOC、AOP的循环依赖

四、源码分析

1. 说说细节

通过上面的例子我们大概了解到,A和B互相依赖时,A创建完后填充属性B,继续创建B,再填充属性A时就可以从缓存中获取了,如下:

如何理解Spring Bean IOC、AOP的循环依赖

那这个解决循环依赖的事放到 Spring 中是什么样呢?展开细节!

虽然,解决循环依赖的核心原理一样,但要放到支撑起整个 Spring 中 IOC、AOP 特性时,就会变得复杂一些,整个处理 Spring  循环依赖的过程如下;

如何理解Spring Bean IOC、AOP的循环依赖

2. 处理过程

关于本章节涉及到的案例源码分析,已更新到 github:https://github.com/fuzhengwei/interview -  interview-31

以下是单元测试中对AB依赖的获取Bean操作,重点在于进入 getBean 的源码跟进;

@Test public void test_alias() {     BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");     Bean_A bean_a = beanFactory.getBean("bean_a", Bean_A.class);     logger.info("获取 Bean 通过别名:{}", bean_a.getBean_b()); }

org.springframework.beans.factory.support.AbstractBeanFactory.java

@Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException {  return doGetBean(name, requiredType, null, false); }

doGetBean 方法

protected <T> T doGetBean(   final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)   throws BeansException {     // 从缓存中获取 bean 实例  Object sharedInstance = getSingleton(beanName);      // mbd.isSingleton() 用于判断 bean 是否是单例模式    if (mbd.isSingleton()) {      // 获取 bean 实例     sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {      @Override      public Object getObject() throws BeansException {       try {         // 创建 bean 实例,createBean 返回的 bean 实例化好的        return createBean(beanName, mbd, args);       }       catch (BeansException ex) {        destroySingleton(beanName);        throw ex;       }      }     });     // 后续的处理操作     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);    }      // ...    // 返回 bean 实例  return (T) bean; }

doCreateBean 方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)   throws BeanCreationException {      // 创建 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回   instanceWrapper = createBeanInstance(beanName, mbd, args);     // 添加 bean 工厂对象到 singletonFactories 缓存中   addSingletonFactory(beanName, new ObjectFactory<Object>() {    @Override    public Object getObject() throws BeansException {      // 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。     return getEarlyBeanReference(beanName, mbd, bean);    }   });     try {    // 填充属性,解析依赖关系   populateBean(beanName, mbd, instanceWrapper);   if (exposedObject != null) {    exposedObject = initializeBean(beanName, exposedObject, mbd);   }  }    // 返回 bean 实例  return exposedObject; }

getSingleton 三级缓存

protected Object getSingleton(String beanName, boolean allowEarlyReference) {   // 从 singletonObjects 获取实例,singletonObjects 是成品 bean  Object singletonObject = this.singletonObjects.get(beanName);  // 判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在创建中  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {   synchronized (this.singletonObjects) {     // 从 earlySingletonObjects 中获取提前曝光未成品的 bean    singletonObject = this.earlySingletonObjects.get(beanName);    if (singletonObject == null && allowEarlyReference) {      // 获取相应的 bean 工厂     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);     if (singletonFactory != null) {       // 提前曝光 bean 实例,主要用于解决AOP循环依赖      singletonObject = singletonFactory.getObject();            // 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除      this.earlySingletonObjects.put(beanName, singletonObject);      this.singletonFactories.remove(beanName);     }    }   }  }  return (singletonObject != NULL_OBJECT ? singletonObject : null); }

综上,是一个处理循环依赖的代码流程,这部分提取出来的内容主要为核心内容,并没有长篇大论的全部拆取出来,大家在调试的时候会涉及的比较多,尽可能要自己根据流程图操作调试几遍。

3. 依赖解析

综上从我们自己去尝试解决循环依赖,学习了循环依赖的核心解决原理。又分析了 Spring  解决的循环依赖的处理过程以及核心源码的分析。那么接下来我们在总结下三级缓存分别不同的处理过程,算是一个总结,也方便大家理解。

1.  一级缓存能解决吗?

如何理解Spring Bean IOC、AOP的循环依赖

2. 二级缓存能解决吗?

如何理解Spring Bean IOC、AOP的循环依赖

3. 三级缓存解决什么?

如何理解Spring Bean IOC、AOP的循环依赖

有了二级缓存都能解决 Spring 依赖了,怎么要有三级缓存呢。其实我们在前面分析源码时也提到过,三级缓存主要是解决 Spring  AOP 的特性。AOP 本身就是对方法的增强,是 ObjectFactory 类型的 lambda 表达式,而 Spring 的原则又不希望将此类类型的  Bean 前置创建,所以要存放到三级缓存中处理。

其实整体处理过程类似,唯独是 B 在填充属性 A  时,先查询成品缓存、再查半成品缓存,最后在看看有没有单例工程类在三级缓存中。最终获取到以后调用 getObject 方法返回代理引用或者原始引用。

至此也就解决了 Spring AOP 所带来的三级缓存问题。

到此,关于“如何理解Spring Bean IOC、AOP的循环依赖”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. Spring IoC、面向切面编程、事务管理等Spring基本概念详解
  2. 一篇告诉你什么是Spring

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring bean aop

上一篇:VBS如何实现加密解密

下一篇:什么是Istio

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》