烂了大街的 Spring 循环依赖问题,你以为本身会了吗

文章已收录在 GitHub JavaKeeper ,N 线互联网开发、面试必备技能兵器谱,笔记自取。

微信搜「 JavaKeeper 」程序员成长充电站,互联网技术武道场。无套路领取 500+ 本电子书和 30+ 视频教学和源码。java

前言

循环依赖问题,算是一道烂大街的面试题了,解毒以前,咱们先来回顾两个知识点:git

初学 Spring 的时候,咱们就知道 IOC,控制反转么,它将本来在程序中手动建立对象的控制权,交由 Spring 框架来管理,不须要咱们手动去各类 new XXX程序员

尽管是 Spring 管理,不也得建立对象吗, Java 对象的建立步骤不少,能够 new XXX、序列化、clone() 等等, 只是 Spring 是经过反射 + 工厂的方式建立对象并放在容器的,建立好的对象咱们通常还会对对象属性进行赋值,才去使用,能够理解是分了两个步骤。github

好了,对这两个步骤有个印象就行,接着咱们进入循环依赖,先说下循环依赖的概念面试

什么是循环依赖

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间造成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A,造成了循环依赖。更或者是本身依赖本身。它们之间的依赖关系以下:spring

这里以两个类直接相互依赖为例,他们的实现代码可能以下:缓存

public class BeanB {
    private BeanA beanA;
    public void setBeanA(BeanA beanA) {
        this.beanA = beanA;
    }
}

public class BeanA {
    private BeanB beanB;
    public void setBeanB(BeanB beanB) {
        this.beanB = beanB;
    }
}

配置信息以下(用注解方式注入同理,只是为了方便理解,用了配置文件):微信

<bean id="beanA" class="priv.starfish.BeanA">
  <property name="beanB" ref="beanB"/>
</bean>

<bean id="beanB" class="priv.starfish.BeanB">
  <property name="beanA" ref="beanA"/>
</bean>

Spring 启动后,读取如上的配置文件,会按顺序先实例化 A,可是建立的时候又发现它依赖了 B,接着就去实例化 B ,一样又发现它依赖了 A ,这尼玛咋整?无限循环呀架构

Spring “确定”不会让这种事情发生的,如前言咱们说的 Spring 实例化对象分两步,第一步会先建立一个原始对象,只是没有设置属性,能够理解为"半成品"—— 官方叫 A 对象的早期引用(EarlyBeanReference),因此当实例化 B 的时候发现依赖了 A, B 就会把这个“半成品”设置进去先完成实例化,既然 B 完成了实例化,因此 A 就能够得到 B 的引用,也完成实例化了,这其实就是 Spring 解决循环依赖的思想。app

有点懵逼

不理解不要紧,先有个大概的印象,而后咱们从源码来看下 Spring 具体是怎么解决的。

源码解毒

代码版本:5.0.16.RELEASE

在 Spring IOC 容器读取 Bean 配置建立 Bean 实例以前, 必须对它进行实例化。只有在容器实例化后,才能够从 IOC 容器里获取 Bean 实例并使用,循环依赖问题也就是发生在实例化 Bean 的过程当中的,因此咱们先回顾下获取 Bean 的过程。

获取 Bean 流程

Spring IOC 容器中获取 bean 实例的简化版流程以下(排除了各类包装和检查的过程)

大概的流程顺序(能够结合着源码看下,我就不贴了,贴太多的话,呕~呕呕,想吐):

  1. 流程从 getBean 方法开始,getBean 是个空壳方法,全部逻辑直接到 doGetBean 方法中
  2. transformedBeanName 将 name 转换为真正的 beanName(name 多是 FactoryBean 以 & 字符开头或者有别名的状况,因此须要转化下)
  3. 而后经过 getSingleton(beanName) 方法尝试从缓存中查找是否是有该实例 sharedInstance(单例在 Spring 的同一容器只会被建立一次,后续再获取 bean,就直接从缓存获取便可)
  4. 若是有的话,sharedInstance 多是彻底实例化好的 bean,也多是一个原始的 bean,因此再经 getObjectForBeanInstance 处理便可返回
  5. 固然 sharedInstance 也多是 null,这时候就会执行建立 bean 的逻辑,将结果返回

第三步的时候咱们提到了一个缓存的概念,这个就是 Spring 为了解决单例的循环依赖问题而设计的 三级缓存

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

这三级缓存的做用分别是:

  • singletonObjects:完成初始化的单例对象的 cache,这里的 bean 经历过 实例化->属性填充->初始化 以及各类后置处理(一级缓存)
  • earlySingletonObjects:存放原始的 bean 对象(完成实例化可是还没有填充属性和初始化),仅仅能做为指针提早曝光,被其余 bean 所引用,用于解决循环依赖的 (二级缓存)
  • singletonFactories:在 bean 实例化完以后,属性填充以及初始化以前,若是容许提早曝光,Spring 会将实例化后的 bean 提早曝光,也就是把该 bean 转换成 beanFactory 并加入到 singletonFactories(三级缓存)

咱们首先从缓存中试着获取 bean,就是从这三级缓存中查找

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从 singletonObjects 获取实例,singletonObjects 中的实例都是准备好的 bean 实例,能够直接使用
    Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation() 判断当前单例bean是否正在建立中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 一级缓存没有,就去二级缓存找
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 二级缓存也没有,就去三级缓存找
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 三级缓存有的话,就把他移动到二级缓存,.getObject() 后续会讲到
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

若是缓存没有的话,咱们就要建立了,接着咱们以单例对象为例,再看下建立 bean 的逻辑(大括号表示内部类调用方法):

  1. 建立 bean 从如下代码开始,一个匿名内部类方法参数(总以为 Lambda 的方式可读性不如内部类好理解)

    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    getSingleton() 方法内部主要有两个方法

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        // 建立 singletonObject
        singletonObject = singletonFactory.getObject();
        // 将 singletonObject 放入缓存
        addSingleton(beanName, singletonObject);
    }
  2. getObject() 匿名内部类的实现真正调用的又是 createBean(beanName, mbd, args)
  3. 往里走,主要的实现逻辑在 doCreateBean方法,先经过 createBeanInstance 建立一个原始 bean 对象
  4. 接着 addSingletonFactory 添加 bean 工厂对象到 singletonFactories 缓存(三级缓存)
  5. 经过 populateBean 方法向原始 bean 对象中填充属性,并解析依赖,假设这时候建立 A 以后填充属性时发现依赖 B,而后建立依赖对象 B 的时候又发现依赖 A,仍是一样的流程,又去 getBean(A),这个时候三级缓存已经有了 beanA 的“半成品”,这时就能够把 A 对象的原始引用注入 B 对象(并将其移动到二级缓存)来解决循环依赖问题。这时候 getObject() 方法就算执行结束了,返回彻底实例化的 bean
  6. 最后调用 addSingleton 把彻底实例化好的 bean 对象放入 singletonObjects 缓存(一级缓存)中,打完收工

Spring 解决循环依赖

建议搭配着“源码”看下边的逻辑图,更好下饭

流程其实上边都已经说过了,结合着上图咱们再看下具体细节,用大白话再捋一捋:

  1. Spring 建立 bean 主要分为两个步骤,建立原始 bean 对象,接着去填充对象属性和初始化
  2. 每次建立 bean 以前,咱们都会从缓存中查下有没有该 bean,由于是单例,只能有一个
  3. 当咱们建立 beanA 的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了 beanB,接着就又去建立 beanB,一样的流程,建立完 beanB 填充属性时又发现它依赖了 beanA,又是一样的流程,不一样的是,这时候能够在三级缓存中查到刚放进去的原始对象 beanA,因此不须要继续建立,用它注入 beanB,完成 beanB 的建立
  4. 既然 beanB 建立好了,因此 beanA 就能够完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成

这就是单例模式下 Spring 解决循环依赖的流程了。

可是这个地方,不论是谁看源码都会有个小疑惑,为何须要三级缓存呢,我赶脚二级他也够了呀

革命还没有成功,同志仍需努力

跟源码的时候,发如今建立 beanB 须要引用 beanA 这个“半成品”的时候,就会触发"前期引用",即以下代码:

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    // 三级缓存有的话,就把他移动到二级缓存
    singletonObject = singletonFactory.getObject();
    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
}

singletonFactory.getObject() 是一个接口方法,这里具体的实现方法在

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                // 这么一大段就这句话是核心,也就是当bean要进行提早曝光时,
                // 给一个机会,经过重写后置处理器的getEarlyBeanReference方法,来自定义操做bean
                // 值得注意的是,若是提早曝光了,可是没有被提早引用,则该后置处理器并不生效!!!
                // 这也正式三级缓存存在的意义,不然二级缓存就能够解决循环依赖的问题
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

这个方法就是 Spring 为何使用三级缓存,而不是二级缓存的缘由,它的目的是为了后置处理,若是没有 AOP 后置处理,就不会走进 if 语句,直接返回了 exposedObject ,至关于啥都没干,二级缓存就够用了。

因此又得出结论,这个三级缓存应该和 AOP 有关系,继续。

在 Spring 的源码中getEarlyBeanReferenceSmartInstantiationAwareBeanPostProcessor 接口的默认方法,真正实现这个方法的只有AbstractAutoProxyCreator 这个类,用于提早曝光的 AOP 代理。

@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   this.earlyProxyReferences.put(cacheKey, bean);
   // 对bean进行提早Spring AOP代理
   return wrapIfNecessary(bean, beanName, cacheKey);
}

这么说有点干,来个小 demo 吧,咱们都知道 Spring AOP、事务等都是经过代理对象来实现的,而事务的代理对象是由自动代理建立器来自动完成的。也就是说 Spring 最终给咱们放进容器里面的是一个代理对象,而非原始对象,假设咱们有以下一段业务代码:

@Service
public class HelloServiceImpl implements HelloService {
   @Autowired
   private HelloService helloService;

   @Override
   @Transactional
   public Object hello() {
      return "Hello JavaKeeper";
   }
}

Service 类使用到了事务,因此最终会生成一个 JDK 动态代理对象 Proxy。恰好它又存在本身引用本身的循环依赖,完美符合咱们的场景需求。

咱们再自定义一个后置处理,来看下效果:

@Component
public class HelloProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("提早曝光了:"+beanName);
        return bean;
    }
}

能够看到,调用方法栈中有咱们本身实现的 HelloProcessor,说明这个 bean 会经过 AOP 代理处理。

再从源码看下这个本身循环本身的 bean 的建立流程:

protected Object doCreateBean( ... ){
    ...
    
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    // 须要提早暴露(支持循环依赖),就注册一个ObjectFactory到三级缓存
    if (earlySingletonExposure) { 
        // 添加 bean 工厂对象到 singletonFactories 缓存中,并获取原始对象的早期引用
        //匿名内部方法 getEarlyBeanReference 就是后置处理器    
        // SmartInstantiationAwareBeanPostProcessor 的一个方法,
        // 它的功效为:保证本身被循环依赖的时候,即便被别的Bean @Autowire进去的也是代理对象
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 此处注意:若是此处本身被循环依赖了  那它会走上面的getEarlyBeanReference,从而建立一个代理对象从        三级缓存转移到二级缓存里
    // 注意此时候对象还在二级缓存里,并无在一级缓存。而且此时后续的这两步操做仍是用的 exposedObject,它仍旧是原始对象~~~
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    // 由于事务的AOP自动代理建立器在getEarlyBeanReference 建立代理后,initializeBean 就不会再重复建立了,二选一的)
        
    // 因此通过这两大步后,exposedObject 仍是原始对象,经过 getEarlyBeanReference 建立的代理对象还在三级缓存呢
    
    ...
    
    // 循环依赖校验
    if (earlySingletonExposure) {
        // 注意此处第二个参数传的false,表示不去三级缓存里再去调用一次getObject()方法了~~~,此时代理对象还在二级缓存,因此这里拿出来的就是个 代理对象
        // 最后赋值给exposedObject  而后return出去,进而最终被addSingleton()添加进一级缓存里面去  
        // 这样就保证了咱们容器里 最终其实是代理对象,而非原始对象~~~~~
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) { 
                exposedObject = earlySingletonReference;
            }
        }
        ...
    }
    
}

自我解惑:

问:仍是不太懂,为何这么设计呢,即便有代理,在二级缓存代理也能够吧 | 为何要使用三级缓存呢?

咱们再来看下相关代码,假设咱们如今是二级缓存架构,建立 A 的时候,咱们不知道有没有循环依赖,因此放入二级缓存提早暴露,接着建立 B,也是放入二级缓存,这时候发现又循环依赖了 A,就去二级缓存找,是有,可是若是此时还有 AOP 代理呢,咱们要的是代理对象可不是原始对象,这怎么办,只能改逻辑,在第一步的时候,无论3721,全部 Bean 通通去完成 AOP 代理,若是是这样的话,就不须要三级缓存了,可是这样不只没有必要,并且违背了 Spring 在结合 AOP 跟 Bean 的生命周期的设计。

因此 Spring “画蛇添足”的将实例先封装到 ObjectFactory 中(三级缓存),主要关键点在 getObject() 方法并不是直接返回实例,而是对实例又使用 SmartInstantiationAwareBeanPostProcessorgetEarlyBeanReference 方法对 bean 进行处理,也就是说,当 Spring 中存在该后置处理器,全部的单例 bean 在实例化后都会被进行提早曝光到三级缓存中,可是并非全部的 bean 都存在循环依赖,也就是三级缓存到二级缓存的步骤不必定都会被执行,有可能曝光后直接建立完成,没被提早引用过,就直接被加入到一级缓存中。所以能够确保只有提早曝光且被引用的 bean 才会进行该后置处理。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
             // 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储                    //getObject()方法用于获取提早曝光的实例
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 三级缓存有的话,就把他移动到二级缓存
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}


boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   // 添加 bean 工厂对象到 singletonFactories 缓存中,并获取原始对象的早期引用
   //匿名内部方法 getEarlyBeanReference 就是后置处理器
   // SmartInstantiationAwareBeanPostProcessor 的一个方法,
   // 它的功效为:保证本身被循环依赖的时候,即便被别的Bean @Autowire进去的也是代理对象~~~~  AOP自动代理建立器此方法里会建立的代理对象~~~
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
再问:AOP 代理对象提早放入了三级缓存,没有通过属性填充和初始化,这个代理又是如何保证依赖属性的注入的呢?

这个又涉及到了 Spring 中动态代理的实现,不论是cglib代理仍是jdk动态代理生成的代理类,代理时,会将目标对象 target 保存在最后生成的代理 $proxy 中,当调用 $proxy 方法时会回调 h.invoke,而 h.invoke 又会回调目标对象 target 的原始方法。全部,其实在 AOP 动态代理时,原始 bean 已经被保存在 提早曝光代理中了,以后 原始 bean 继续完成属性填充初始化操做。由于 AOP 代理$proxy 中保存着 traget 也就是是 原始bean 的引用,所以后续 原始bean 的完善,也就至关于Spring AOP中的 target 的完善,这样就保证了 AOP 的属性填充初始化了!

非单例循环依赖

看完了单例模式的循环依赖,咱们再看下非单例的状况,假设咱们的配置文件是这样的:

<bean id="beanA" class="priv.starfish.BeanA" scope="prototype">
   <property name="beanB" ref="beanB"/>
</bean>

<bean id="beanB" class="priv.starfish.BeanB" scope="prototype">
   <property name="beanA" ref="beanA"/>
</bean>

启动 Spring,结果以下:

Error creating bean with name 'beanA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'beanB' while setting bean property 'beanB';

Error creating bean with name 'beanB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'beanA' while setting bean property 'beanA';

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

对于 prototype 做用域的 bean,Spring 容器没法完成依赖注入,由于 Spring 容器不进行缓存 prototype 做用域的 bean ,所以没法提早暴露一个建立中的bean 。

缘由也挺好理解的,原型模式每次请求都会建立一个实例对象,即便加了缓存,循环引用太多的话,就比较麻烦了就,因此 Spring 不支持这种方式,直接抛出异常:

if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
}

构造器循环依赖

上文咱们讲的是经过 Setter 方法注入的单例 bean 的循环依赖问题,用 Spring 的小伙伴也都知道,依赖注入的方式还有构造器注入、工厂方法注入的方式(不多使用),那若是构造器注入方式也有循环依赖,能够搞不?

咱们再改下代码和配置文件

public class BeanA {
   private BeanB beanB;
   public BeanA(BeanB beanB) {
      this.beanB = beanB;
   }
}

public class BeanB {
    private BeanA beanA;
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}
<bean id="beanA" class="priv.starfish.BeanA">
<constructor-arg ref="beanB"/>
</bean>

<bean id="beanB" class="priv.starfish.BeanB">
<constructor-arg ref="beanA"/>
</bean>

执行结果,又是异常

看看官方给出的说法

Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).

大概意思是:

若是您主要使用构造器注入,循环依赖场景是没法解决的。建议你用 setter 注入方式代替构造器注入

其实也不是说只要是构造器注入就会有循环依赖问题,Spring 在建立 Bean 的时候默认是按照天然排序来进行建立的,咱们暂且把先建立的 bean 叫主 bean,上文的 A 即主 bean,只要主 bean 注入依赖 bean 的方式是 setter 方式,依赖 bean 的注入方式无所谓,均可以解决,反之亦然

因此上文咱们 AB 循环依赖问题,只要 A 的注入方式是 setter ,就不会有循环依赖问题。

面试官问:为何呢?

Spring 解决循环依赖依靠的是 Bean 的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。实例化的过程又是经过构造器建立的,若是 A 还没建立好出来,怎么可能提早曝光,因此构造器的循环依赖没法解决,我一直认为应该先有鸡才能有蛋

小总结 | 面试这么答

B 中提早注入了一个没有通过初始化的 A 类型对象不会有问题吗?

虽然在建立 B 时会提早给 B 注入了一个还未初始化的 A 对象,可是在建立 A 的流程中一直使用的是注入到 B 中的 A 对象的引用,以后会根据这个引用对 A 进行初始化,因此这是没有问题的。

Spring 是如何解决的循环依赖?

Spring 为了解决单例的循环依赖问题,使用了三级缓存。其中一级缓存为单例池(singletonObjects),二级缓存为提早曝光对象(earlySingletonObjects),三级缓存为提早曝光对象工厂(singletonFactories)。

假设A、B循环引用,实例化 A 的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了 B,一样的流程也是实例化后放入三级缓存,接着去填充属性时又发现本身依赖 A,这时候从缓存中查找到早期暴露的 A,没有 AOP 代理的话,直接将 A 的原始对象注入 B,完成 B 的初始化后,进行属性填充和初始化,这时候 B 完成后,就去完成剩下的 A 的步骤,若是有 AOP 代理,就进行 AOP 处理获取代理后的对象 A,注入 B,走剩下的流程。

为何要使用三级缓存呢?二级缓存能解决循环依赖吗?

若是没有 AOP 代理,二级缓存能够解决问题,可是有 AOP 代理的状况下,只用二级缓存就意味着全部 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 设计的原则,Spring 在设计之初就是经过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来在 Bean 生命周期的最后一步来完成 AOP 代理,而不是在实例化后就立马进行 AOP 代理。

参考与感谢:

《Spring 源码深度解析》- 郝佳著

https://developer.aliyun.com/...

http://www.tianxiaobo.com/201...

https://cloud.tencent.com/dev...

https://blog.csdn.net/chaitou...

相关文章
相关标签/搜索