1. 手写spring循环依赖的整个过程java
2. spring怎么解决循环依赖面试
3. 为何要二级缓存和三级缓存spring
4. spring有没有解决构造函数的循环依赖缓存
5. spring有没有解决多例下的循环依赖.多线程
以下图所示: ide
A类依赖了B类, 同时B类有依赖了A类. 这就是循环依赖, 造成了一个闭环函数
如上图: A依赖了B, B同时依赖了A和C , C依赖了A. 这也是循环依赖. , 造成了一个闭环测试
那么, 若是出现循环依赖, spring是如何解决循环依赖问题的呢?this
咱们定义三个类:spa
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class InstanceA { @Autowired private InstanceB instanceB; public InstanceA() { System.out.println("调用 instanceA的构造函数"); } public InstanceA(InstanceB instanceB) { this.instanceB = instanceB; } public void say(){ System.out.println( "I am A"); } public InstanceB getInstanceB() { return instanceB; } public void setInstanceB(InstanceB instanceB) { this.instanceB = instanceB; } }
这是InstanceA, 里面引用了InstanceB.
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class InstanceB { @Autowired private InstanceA instanceA; public InstanceB() { System.out.println("调用 instanceB的构造函数"); } public InstanceA getInstanceA() { return instanceA; } public void setInstanceA(InstanceA instanceA) { this.instanceA = instanceA; } }
这是InstanceB, 在里面有引用了InstanceA
这个在前面已经说过了, 首先会加载配置类的后置处理器, 将其解析后放入到beanDefinitionMap中. 而后加载配置类, 也将其解析后放入beanDefinitionMap中. 最后解析配置类. 咱们这里直接简化掉前两步, 将两个类放入beanDefinitionMap中. 主要模拟第三步解析配置类. 在解析的过程当中, 获取bean的时候会出现循环依赖的问题循环依赖.
public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/** * 读取bean定义, 固然在spring中确定是根据配置 动态扫描注册的 * * InstanceA和InstanceB都有注解@Component, 因此, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中. * 这里, 咱们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中. */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // 第一步: 扫描配置类, 读取bean定义 loadBeanDefinitions(); ...... }
上面的代码结构很简单, 再看一下注释应该就能明白了. 这里就是模拟spring将配置类解析放入到beanDefinitionMap的过程.
首先,咱们已经知道, 建立bean一共有三个步骤: 实例化, 属性赋值, 初始化.
而在属性赋值的时候, 会判断是否引用了其余的Bean, 若是引用了, 那么须要构建此Bean. 下面来看一下代码
/** * 获取bean, 根据beanName获取 */ public static Object getBean(String beanName) throws Exception {/** * 第一步: 实例化 * 咱们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数 */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // 调用无参的构造函数进行实例化 Object instanceBean = beanClass.newInstance(); /** * 第二步: 属性赋值 * instanceA这类类里面有一个属性, InstanceB. 因此, 先拿到 instanceB, 而后在判断属性头上有没有Autowired注解. * 注意: 这里咱们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // 判断每个属性是否有@Autowired注解 Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // 设置这个属性是可访问的 declaredField.setAccessible(true); // 那么这个时候还要构建这个属性的bean. /* * 获取属性的名字 * 真实状况, spring这里会判断, 是根据名字, 仍是类型, 仍是构造函数来获取类. * 咱们这里模拟, 因此简单一些, 直接根据名字获取. */ String name = declaredField.getName(); /** * 这样, 在这里咱们就拿到了 instanceB 的 bean */ Object fileObject = getBean(name); // 为属性设置类型 declaredField.set(instanceBean, fileObject); } } /** * 第三步: 初始化 * 初始化就是设置类的init-method.这个能够设置也能够不设置. 咱们这里就不设置了 */ return instanceBean; }
咱们看到如上代码.
第一步: 实例化: 使用反射的方式, 根据beanName查找构建一个实例bean.
第二步: 属性赋值: 判断属性中是否有@Autowired属性, 若是有这个属性, 那么须要构建bean. 咱们发如今为InstanceA赋值的时候, 里面引用了InstanceB, 因此去建立InstanceB, 而建立InstanceB的时候, 发现里面又有InstanceA, 因而又去建立A. 而后以此类推,继续判断. 就造成了死循环. 没法走出这个环. 这就是循环依赖
第三步: 初始化: 调用init-method, 这个方法不是必须有, 因此,咱们这里不模拟了
看看以下图所示
红色部分就造成了循环依赖.
咱们知道上面进行了循环依赖了. 其实, 咱们的目标很简单, 若是一个类建立过了, 那么就请不要在建立了.
因此, 咱们增长一级缓存
// 一级缓存 private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); /** * 获取bean, 根据beanName获取 */ public static Object getBean(String beanName) throws Exception { // 增长一个出口. 判断实体类是否已经被加载过了 Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * 第一步: 实例化 * 咱们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数 */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // 调用无参的构造函数进行实例化 Object instanceBean = beanClass.newInstance(); /** * 第二步: 放入到一级缓存 */ singletonObjects.put(beanName, instanceBean); /** * 第三步: 属性赋值 * instanceA这类类里面有一个属性, InstanceB. 因此, 先拿到 instanceB, 而后在判断属性头上有没有Autowired注解. * 注意: 这里咱们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // 判断每个属性是否有@Autowired注解 Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // 设置这个属性是可访问的 declaredField.setAccessible(true); // 那么这个时候还要构建这个属性的bean. /* * 获取属性的名字 * 真实状况, spring这里会判断, 是根据名字, 仍是类型, 仍是构造函数来获取类. * 咱们这里模拟, 因此简单一些, 直接根据名字获取. */ String name = declaredField.getName(); /** * 这样, 在这里咱们就拿到了 instanceB 的 bean */ Object fileObject = getBean(name); // 为属性设置类型 declaredField.set(instanceBean, fileObject); } } /** * 第四步: 初始化 * 初始化就是设置类的init-method.这个能够设置也能够不设置. 咱们这里就不设置了 */ return instanceBean; }
仍是上面的获取bean的流程, 不同的是, 这里增长了以及缓存. 当咱们获取到bean实例之后, 将其放入到缓存中. 下次再须要建立以前, 先去缓存里判断,是否已经有了, 若是没有, 那么再建立.
这样就给建立bean增长了一个出口. 不会循环建立了.
如上图所示, 在@Autowired的时候, 增长了一个出口. 判断即将要建立的类是否已经存在, 若是存在了, 那么就直接返回, 不在建立
虽然使用了一级缓存解决了循环依赖的问题, 但要是在多线程下, 这个依赖可能就会出现问题.
好比: 有两个线程, 同时建立instanceA 和instanceB, instanceA和instanceB都引用了instanceC. 他们同步进行, 都去建立instanceC. 首先A去建立, A在实例化instanceC之后就将其放入到一级缓存了, 这时候, B去一级缓存里拿. 此时拿到的instanceC是不完整的. 后面的属性赋值, 初始化都尚未执行呢. 因此, 咱们增长耳机缓存来解决这个问题.
public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // 一级缓存 private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 二级缓存 private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); /** * 读取bean定义, 固然在spring中确定是根据配置 动态扫描注册的 * * InstanceA和InstanceB都有注解@Component, 因此, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中. * 这里, 咱们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中. */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // 第一步: 扫描配置类, 读取bean定义 loadBeanDefinitions(); // 第二步: 循环建立bean for (String key: beanDefinitionMap.keySet()) { // 第一次: key是instanceA, 因此先建立A类 getBean(key); } // 测试: 看是否能执行成功 InstanceA instanceA = (InstanceA) getBean("instanceA"); instanceA.say(); } /** * 获取bean, 根据beanName获取 */ public static Object getBean(String beanName) throws Exception { // 增长一个出口. 判断实体类是否已经被加载过了 Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * 第一步: 实例化 * 咱们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数 */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // 调用无参的构造函数进行实例化 Object instanceBean = beanClass.newInstance(); /** * 第二步: 放入到二级缓存 */ earlySingletonObjects.put(beanName, instanceBean); /** * 第三步: 属性赋值 * instanceA这类类里面有一个属性, InstanceB. 因此, 先拿到 instanceB, 而后在判断属性头上有没有Autowired注解. * 注意: 这里咱们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // 判断每个属性是否有@Autowired注解 Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // 设置这个属性是可访问的 declaredField.setAccessible(true); // 那么这个时候还要构建这个属性的bean. /* * 获取属性的名字 * 真实状况, spring这里会判断, 是根据名字, 仍是类型, 仍是构造函数来获取类. * 咱们这里模拟, 因此简单一些, 直接根据名字获取. */ String name = declaredField.getName(); /** * 这样, 在这里咱们就拿到了 instanceB 的 bean */ Object fileObject = getBean(name); // 为属性设置类型 declaredField.set(instanceBean, fileObject); } } /** * 第四步: 初始化 * 初始化就是设置类的init-method.这个能够设置也能够不设置. 咱们这里就不设置了 */ /** * 第二步: 放入到一级缓存 */ singletonObjects.put(beanName, instanceBean); return instanceBean; } /** * 判断是不是循环引用的出口. * @param beanName * @return */ private static Object getSingleton(String beanName) { // 先去一级缓存里拿,若是一级缓存没有拿到,去二级缓存里拿 if (singletonObjects.containsKey(beanName)) { return singletonObjects.get(beanName); } else if (earlySingletonObjects.containsKey(beanName)){ return earlySingletonObjects.get(beanName); } else { return null; } } }
如上图所示,增长了一个二级缓存. 首先, 构建出instanceBean之后, 直接将其放入到二级缓存中. 这时只是一个纯净的bean, 里面尚未给属性赋值, 初始化. 在给属性赋值完成, 初始化完成之后, 在将其放入到一级缓存中.
咱们判断缓存中是否有某个实例bean的时候, 先去一级缓存中判断是否有完整的bean, 若是没有, 就去二级缓存中判断有没有实例化过这个bean.
总结: 一级缓存和二级缓存的做用
一级缓存: 解决循环依赖的问题
二级缓存: 在建立实例bean和放入到一级缓存之间还有一段间隙. 若是在这之间从一级缓存拿实例, 确定是返回null的. 为了不这个问题, 增长了二级缓存.
咱们都知道spring中有一级缓存, 二级缓存, 三级缓存. 一级缓存和二级缓存的做用咱们知道了, 那么三级缓存有什么用呢?
三级缓存有什么做用呢? 这个问题众说纷纭, 有说代理, 有说AOP. 其实AOP的问题能够用二级缓存来解决. 下面就来看看AOP如何用二级缓存解决.
建立AOP动态代理 (不是耦合的, 采用解耦的, 经过BeanPostProcessor bean的后置处理器来建立). 以前讲过, 以下图
在初始化以后, 调用Bean的后置处理器去建立的AOP的动态代理
如上图. 咱们在建立bean 的时候, 会有不少Bean的后置处理器BeanPostProcessor. 若是有AOP, 会在何时建立呢? 在初始化之后, 调用BeanPostProcessor建立动态代理.
结合上面的代码, 咱们想想, 其实在初始化之后建立动态代理就晚了. 为何呢? 由于, 若是有循环依赖, 在初始化以后才调用, 那就不是动态代理. 其实咱们这时候应该在实例化以后, 放入到二级缓存以前调用
面试题: 在建立bean的时候, 在哪里建立的动态代理, 这个应该怎么回答呢? 不少人会说在初始化以后, 或者在实例化以后. 其实更严谨的说, 有两种状况: 第一种是在初始化以后调用 . 第二种是出现了循环依赖, 会在实例化以后调用
咱们上面说的就是第二种状况. 也就是说,正常状况下是在初始化以后调用的, 可是若是有循环依赖, 就要在实例化以后调用了.
下面来看看如何在二级缓存加动态代理.
首先, 咱们这里有循环依赖, 因此将动态代理放在实例化以后,
/** * 获取bean, 根据beanName获取 */ public static Object getBean(String beanName) throws Exception { // 增长一个出口. 判断实体类是否已经被加载过了 Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } /** * 第一步: 实例化 * 咱们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数 */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // 调用无参的构造函数进行实例化 Object instanceBean = beanClass.newInstance(); /** * 建立AOP动态代理 (不是耦合的, 采用解耦的, 经过BeanPostProcessor bean的后置处理器得来的. 以前讲过, * 在初始化以后, 调用Bean的后置处理器去建立的AOP的动态代理 ) */ instanceBean = new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, "instanceA"); /** * 第二步: 放入到二级缓存 */ earlySingletonObjects.put(beanName, instanceBean); /** * 第三步: 属性赋值 * instanceA这类类里面有一个属性, InstanceB. 因此, 先拿到 instanceB, 而后在判断属性头上有没有Autowired注解. * 注意: 这里咱们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // 判断每个属性是否有@Autowired注解 Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // 设置这个属性是可访问的 declaredField.setAccessible(true); // 那么这个时候还要构建这个属性的bean. /* * 获取属性的名字 * 真实状况, spring这里会判断, 是根据名字, 仍是类型, 仍是构造函数来获取类. * 咱们这里模拟, 因此简单一些, 直接根据名字获取. */ String name = declaredField.getName(); /** * 这样, 在这里咱们就拿到了 instanceB 的 bean */ Object fileObject = getBean(name); // 为属性设置类型 declaredField.set(instanceBean, fileObject); } } /** * 第四步: 初始化 * 初始化就是设置类的init-method.这个能够设置也能够不设置. 咱们这里就不设置了 */ // 正常动态代理建立的时机 /** * 第五步: 放入到一级缓存 */ singletonObjects.put(beanName, instanceBean); return instanceBean; }
这里只是简单模拟了动态代理.
咱们知道动态代理有两个地方. 若是是普通类动态代理在初始化以后执行, 若是是循环依赖, 那么动态代理是在实例化以后.
上面在实例化以后建立proxy的代码不完整, 为何不完整呢, 由于没有判断是不是循环依赖.
咱们简单模拟一个动态代理的实现.
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { /** * 假设A被切点命中 须要建立代理 @PointCut("execution(* *..InstanceA.*(..))") * @param bean the raw bean instance * @param beanName the name of the bean * @return * @throws BeansException */ @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { // 假设A被切点命中 须要建立代理 @PointCut("execution(* *..InstanceA.*(..))") /** * 这里, 咱们简单直接判断bean是否是InstanceA实例, 若是是, 就建立动态代理. * 这里没有去解析切点, 解析切点是AspectJ作的事. */ if (bean instanceof InstanceA) { JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean); return jdkDynimcProxy.getProxy(); } return bean; } }
这里直接判断, 若是bean是InstanceA的实例, 那么就调用bean的动态代理. 动态代理的简单逻辑就是: 解析切面, 而后建立类, 若是类不存在就新增, 若是存在则不在建立, 直接取出来返回.
在来看看动态代理,放在实例化以后. 建立AOP, 可是, 在这里建立AOP动态代理的条件是循环依赖.
二级缓存中bean不是null.
若是一个类在建立的过程当中, 会放入到二级缓存, 若是彻底建立完了, 会放入到一级缓存, 而后删除二级缓存. 因此, 若是二级缓存中的bean只要存在, 就说明这个类是建立中, 出现了循环依赖.
应该在getSingleton()判断是不是循环依赖的时候判断. 由于这时候咱们恰好判断了二级缓存中bean是否为空.
/** * 判断是不是循环引用的出口. * @param beanName * @return */ private static Object getSingleton(String beanName) { // 先去一级缓存里拿,若是一级缓存没有拿到,去二级缓存里拿 if (singletonObjects.containsKey(beanName)) { return singletonObjects.get(beanName); } else if (earlySingletonObjects.containsKey(beanName)){ /** * 第一次建立bean是正常的instanceBean. 他并非循环依赖. 第二次进来判断, 这个bean已经存在了, 就说明是循环依赖了 * 这时候经过动态代理建立bean. 而后将这个bean在放入到二级缓存中覆盖原来的instanceBean. */ Object obj = new JdkProxyBeanPostProcessor() .getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName); earlySingletonObjects.put(beanName, obj); return earlySingletonObjects.get(beanName); } else { return null; } }
这样咱们在循环依赖的时候就完成了AOP的建立. 这是在二级缓存里建立的AOP,
那么,来找问题. 这里有两个问题:
问题1: 咱们发如今建立动态代理的时候, 咱们使用的bean的后置处理器JdkProxyBeanPostProcessor.这有点不太符合规则,
由于, spring在getBean()的时候并无使用Bean的后置处理器, 而是在createBean()的时候才去使用的bean的后置处理器.
问题2: 若是A是AOP, 他一直都是, 最开始建立的时候也应该是. 使用这种方法, 结果是第一次建立出来的bean不是AOP动态代理.
对于第一个问题: 咱们但愿在实例化的时候建立AOP, 可是具体判断是在getSingleton()方法里判断. 这里经过三级缓存来实现. 三级缓存里面放的是一个接口定义的钩子方法. 方法的执行在后面调用的时候执行.
对于第二个问题: 咱们的二级缓存就不能直接保存instanceBean实例了, 增长一个参数, 用来标记当前这个类是一个正在建立中的类. 这样来判断循环依赖.
下面先来看看建立的三个缓存和一个标识
// 一级缓存 private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 二级缓存: 为了将成熟的bean和纯净的bean分离. 避免读取到不完整的bean. private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // 三级缓存: private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>(); // 循环依赖的标识---当前正在建立的实例bean private static Set<String> singletonsCurrectlyInCreation = new HashSet<>();
而后在来看看循环依赖的出口
/** * 判断是不是循环引用的出口. * @param beanName * @return */ private static Object getSingleton(String beanName) { //先去一级缓存里拿 Object bean = singletonObjects.get(beanName); // 一级缓存中没有, 可是正在建立的bean标识中有, 说明是循环依赖 if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // 若是二级缓存中没有, 就从三级缓存中拿 if (bean == null) { // 从三级缓存中取 ObjectFactory objectFactory = singletonFactories.get(beanName); if (objectFactory != null) { // 这里是真正建立动态代理的地方. Object obj = objectFactory.getObject(); // 而后将其放入到二级缓存中. 由于若是有屡次依赖, 就去二级缓存中判断. 已经有了就不在再次建立了 earlySingletonObjects.put(beanName, obj); } } } return bean; }
这里的逻辑是, 先去一级缓存中拿, 一级缓存放的是成熟的bean, 也就是他已经完成了属性赋值和初始化. 若是一级缓存没有, 而正在建立中的类标识是true, 就说明这个类正在建立中, 这是一个循环依赖. 这个时候就去二级缓存中取数据, 二级缓存中的数据是什么时候放进去的呢, 是后面从三级缓存中建立动态代理后放进去的. 若是二级缓存为空, 说明没有建立过动态代理, 这时候在去三级缓存中拿, 而后建立动态代理. 建立完之后放入二级缓存中, 后面就不用再建立.
完成的代码以下:
package com.lxl.www.circulardependencies; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowire; 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.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class MainStart { private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // 一级缓存 private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 二级缓存: 为了将成熟的bean和纯净的bean分离. 避免读取到不完整的bean. private static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // 三级缓存: private static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>(); // 循环依赖的标识---当前正在建立的实例bean private static Set<String> singletonsCurrectlyInCreation = new HashSet<>(); /** * 读取bean定义, 固然在spring中确定是根据配置 动态扫描注册的 * * InstanceA和InstanceB都有注解@Component, 因此, 在spring扫描读取配置类的时候, 会把他们两个扫描到BeanDefinitionMap中. * 这里, 咱们省略这一步, 直接将instanceA和instanceB放到BeanDefinitionMap中. */ public static void loadBeanDefinitions(){ RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class); RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class); beanDefinitionMap.put("instanceA", aBeanDefinition); beanDefinitionMap.put("instanceB", bBeanDefinition); } public static void main(String[] args) throws Exception { // 第一步: 扫描配置类, 读取bean定义 loadBeanDefinitions(); // 第二步: 循环建立bean for (String key: beanDefinitionMap.keySet()) { // 第一次: key是instanceA, 因此先建立A类 getBean(key); } // 测试: 看是否能执行成功 InstanceA instanceA = (InstanceA) getBean("instanceA"); instanceA.say(); } /** * 获取bean, 根据beanName获取 */ public static Object getBean(String beanName) throws Exception { // 增长一个出口. 判断实体类是否已经被加载过了 Object singleton = getSingleton(beanName); if (singleton != null) { return singleton; } // 标记bean正在建立 if (!singletonsCurrectlyInCreation.contains(beanName)) { singletonsCurrectlyInCreation.add(beanName); } /** * 第一步: 实例化 * 咱们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数 */ RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName); Class<?> beanClass = beanDefinition.getBeanClass(); // 调用无参的构造函数进行实例化 Object instanceBean = beanClass.newInstance(); /** * 第二步: 放入到三级缓存 * 每一次createBean都会将其放入到三级缓存中. getObject是一个钩子方法. 在这里不会被调用. * 何时被调用呢? * 在getSingleton()从三级缓存中取数据, 调用建立动态代理的时候 */ singletonFactories.put(beanName, new ObjectFactory() { @Override public Object getObject() throws BeansException { return new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName); } }); //earlySingletonObjects.put(beanName, instanceBean); /** * 第三步: 属性赋值 * instanceA这类类里面有一个属性, InstanceB. 因此, 先拿到 instanceB, 而后在判断属性头上有没有Autowired注解. * 注意: 这里咱们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type */ Field[] declaredFields = beanClass.getDeclaredFields(); for (Field declaredField: declaredFields) { // 判断每个属性是否有@Autowired注解 Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation != null) { // 设置这个属性是可访问的 declaredField.setAccessible(true); // 那么这个时候还要构建这个属性的bean. /* * 获取属性的名字 * 真实状况, spring这里会判断, 是根据名字, 仍是类型, 仍是构造函数来获取类. * 咱们这里模拟, 因此简单一些, 直接根据名字获取. */ String name = declaredField.getName(); /** * 这样, 在这里咱们就拿到了 instanceB 的 bean */ Object fileObject = getBean(name); // 为属性设置类型 declaredField.set(instanceBean, fileObject); } } /** * 第四步: 初始化 * 初始化就是设置类的init-method.这个能够设置也能够不设置. 咱们这里就不设置了 */ /** * 第五步: 放入到一级缓存 * * 在这里二级缓存存的是动态代理, 那么一级缓存确定也要存动态代理的实例. * 从二级缓存中取出实例, 放入到一级缓存中 */ if (earlySingletonObjects.containsKey(beanName)) { instanceBean = earlySingletonObjects.get(beanName); } singletonObjects.put(beanName, instanceBean); return instanceBean; } /** * 判断是不是循环引用的出口. * @param beanName * @return */ private static Object getSingleton(String beanName) { //先去一级缓存里拿, Object bean = singletonObjects.get(beanName); // 一级缓存中没有, 可是正在建立的bean标识中有, 说明是循环依赖 if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // 若是二级缓存中没有, 就从三级缓存中拿 if (bean == null) { // 从三级缓存中取 ObjectFactory objectFactory = singletonFactories.get(beanName); if (objectFactory != null) { // 这里是真正建立动态代理的地方. Object obj = objectFactory.getObject(); // 而后将其放入到二级缓存中. 由于若是有屡次依赖, 就去二级缓存中判断. 已经有了就不在再次建立了 earlySingletonObjects.put(beanName, obj); } } } return bean; } }
下面就咱们的代码分析一下:
第一种状况: 没有循环依赖
第二种状况: 有循环依赖
第三种状况: 有屡次循环依赖
咱们模拟一个循环依赖的场景, 覆盖这三种状况.
用代码表示
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; public class A { @Autowired private B b; @Autowired private C c; }
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; public class B { @Autowired private A a; @Autowired private B b; }
package com.lxl.www.circulardependencies; import org.springframework.beans.factory.annotation.Autowired; public class C { @Autowired private A a; }
其中类A恰好匹配AOP的切面@PointCut("execution(* *..A.*(..))")
下面分析他们的循环依赖关系.
此时beanDefinitionMap中有三个bean定义. 分别是A, B, C
1. 先解析类A, 根据上面的流程.
1) 首先调用getSingleton, 此时一级缓存, 二级缓存都没有, 正在建立标志也是null. 因此, 返回的是null
2) 标记当前类正在建立中
3) 实例化
4) 将A放入到三级缓存, 并定义动态代理的钩子方法
5) 属性赋值. A有两个属性, 分别是B和C. 都带有@Autowired注解, 先解析B.
6) A暂停, 解析B
2. 解析A类的属性类B
1) 首先调用getSingleton, 此时一级缓存, 二级缓存都没有, 正在建立标志也是null. 因此, 返回的是null
2) 标记当前类正在建立中
3) 实例化
4) 将B放入到三级缓存, 并定义动态代理的钩子方法
5) 属性赋值. B有两个属性, 分别是A和C. 都带有@Autowired注解, 先解析A. 在解析C
6) B暂停, 解析A
3. 解析B类的属性A
1) 首先调用getSingleton, 此时一级缓存中这个属性为null, 正在建立中标志位true, 二级缓存为空, 从三级缓存中建立动态代理, 而后判断是否符合动态代理切面要求, A符合. 因此经过动态代理建立A的代理bean放入到二级缓存. 返回实例bean.
2) A此时已经存在了, 因此, 直接返回
4. 解析B类的属性C
1) 首先调用getSingleton, 此时一级缓存, 二级缓存都没有, 正在建立标志也是null. 因此, 返回的是null
2) 标记当前类C正在建立中
3) 实例化
4) 将C放入到三级缓存, 并定义动态代理的钩子方法
5) 属性赋值. C有一个属性, 是A. 带有@Autowired注解, 先解析A
6) C暂停, 解析A
5. 解析C中的属性A
1) 首先调用getSingleton()方法, 此时一级缓存中没有, 标志位为true, 二级缓存中已经有A的动态代理实例了, 因此,直接返回.
2) A此时已经在存在, 直接返回
6. 继续解析B类的属性C
1) 接着第4步往下走
2) 初始化类C
3) 将类C放入到一级缓存中. 放以前去二级缓存中取, 二级缓存中没有. 因此, 这里存的是C经过反射构建的instanceBean
7. 继续解析A类的属性类B
1) 接着第2步往下走
2) 初始化类B
3) 将类B放入到一级缓存中. 放以前去二级缓存中取.二级缓存中没有, 因此, 这里存的是B经过反射构建的instanceBean
4) 构建结束,返回
8. 解析A类的属性类C
1) 首先调用getSingleton()方法, 此时一级缓存中已经有了类C, 因此直接返回
9. 继续解析A类
1) 接着第1步往下走
2) 初始化类A
3) 将A放入到一级缓存中. 放以前判断二级缓存中有没有实例bean, 咱们发现有, 因此, 取出来放入到A的一级缓存中.
4) 构建bean结束, 返回
10. 接下来构建beanDefinitionMap中的类B
1) 首先调用getSingleton()方法, 此时一级缓存中已经有了类B, 因此直接返回
11. 接下来构建beanDefinitionMap中的类C
1) 首先调用getSingleton()方法, 此时一级缓存中已经有了类C, 因此直接返回
至此整个构建过程结束.
再来感觉一下三级缓存的做用:
一级缓存: 用来存放成熟的bean. 这个bean若是是切入点, 则是一个动态代理的bean,若是不是切入点, 则是一个普通的类
二级缓存: 用来存放循环依赖过程当中建立的动态代理bean.
三级缓存: 用来存放动态代理的钩子方法. 用来在须要构建动态代理类的时候使用.