spring 源码解析之 doGetBean

前言

经过了前一章的分析(spring 源码解析之 bean 的获取),我们是对 bean 的获取过程有了大致的了解,接下来就是详细剖析每个方法。以及为什么要这样做。

源码解析

transformedBeanName

根据 name获取 beanName,这里的 name 跟 beanName 有什么区别呢,这个 name 可能是普通的 bean名称,也可能是 factoryBean这个特殊的bean名称,factoryBean的名称是带有&的,所以要去掉特殊的符号。

getSingleton

前面已经提到过,单例在Spring 的同一个容器内只会被创建一次,后续再获取 bean 直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从 singletonFactories 中加载。因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 创建bean的原则是不等bean 创建完成就会将创建bean 的 ObjectFactory 提早曝光加人到缓存中,一旦下一个 bean 创建时需要依赖上个 bean,则直接使用 ObjectFactory。这里好多概念如循环依赖、单例缓存、提早曝光等,后面会一一讲清楚,不着急,先混个眼熟。

  • 优先从singletonObjects这个缓存中获取,如果存在则直接返回,
  • 如果不存在则判断该 bean是否正在创建,如果是,则从earlySingletonObjects获取,返回
  • 如果还获取不到,则判断是否运行循环依赖,允许则在singletonFactories这个缓存中获取,返回
  • 如果再获取不到,那就返回 null。
java 复制代码
 @Nullable
 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    // 获取单例 bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果bean 不在缓存中,并且当前正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
       // 获取早期曝光的 bean (二级缓存里的 bean)
       singletonObject = this.earlySingletonObjects.get(beanName);
       // allowEarlyReference 是否可以循环依赖
       if (singletonObject == null && allowEarlyReference) {
          // 因为所有的 bean 缓存都是全局的,因此需要在锁内检查一次
          synchronized (this.singletonObjects) {
             // Consistent creation of early reference within full singleton lock
             singletonObject = this.singletonObjects.get(beanName);
             if (singletonObject == null) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null) {
                   // 如果在三级缓存中存在 则将其从三级缓存中获取 放到二级缓存中 从三级缓存中移除
                   ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                   if (singletonFactory != null) {
                      singletonObject = singletonFactory.getObject();
                      this.earlySingletonObjects.put(beanName, singletonObject);
                      this.singletonFactories.remove(beanName);
                   }
                }
             }
          }
       }
    }
    return singletonObject;
 }

这里最主要的就是这几个缓存是什么东西?这三个缓存就是用来解决循坏依赖的问题,这也是面试会经常被问到的问题。

java 复制代码
 // singleton对象的缓存:bean名称到bean实例
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 ​
 /** Cache of singleton factories: bean name to ObjectFactory. */
 // 单例工厂缓存:bean名称到ObjectFactory HashMap。这是为了解决循环依赖问题,用于提前暴露对象 (三级缓存)
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 ​
 /** Cache of early singleton objects: bean name to bean instance. */
 // 早期曝光的singleton对象的缓存:bean名称到bean实例 (二级缓存)
 private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
   /** Names of beans that are currently in creation. */
   // 当前正在创建的bean的名称
 private final Set<String> singletonsCurrentlyInCreation =
       Collections.newSetFromMap(new ConcurrentHashMap<>(16));

这里有一个问题了,什么是循坏依赖,什么是早期曝光的 bean?下面是简单解释一下,详细了解看一下这篇文章:spring 源码解析番外篇之什么是循环依赖

java 复制代码
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 ​
 @Component
 public class BeanA {
     private final BeanB beanB;
 ​
     // A依赖 B
     @Autowired
     public BeanA(BeanB beanB) {
         this.beanB = beanB;
     }
 }
 ​
 @Component
 public class BeanB {
     private final BeanA beanA;
 ​
     // B 依赖 A
     @Autowired
     public BeanB(BeanA beanA) {
         this.beanA = beanA;
     }
 }
 ​
  • 上面的例子就是循坏依赖的例子,如果先是BeanA先实例化,则会查找该 bean 的依赖 bean。查到依赖 BeanB之后,先将BeanA 暂停实例化,同时会将 BeanA的引用ObjectFactory缓存到singletonFactories之中,然后就会优先将 BeanB 实例化完,再对 BeanA 继续实例化。

  • 由于 BeanA 暂停实例化了,优先 BeanB 进行实例化,那么重复上面的步骤,BeanB 会找到 BeanA 的依赖,又优先实例化 BeanA,如此就会进入一个死循环,所以为了解决这个问题,spring 就创建了上面那三个缓存

  • 当实例化BeanB时,查询 BeanA 依赖同样会优先尝试从 singletonObjects 里面获取实例,如果获取不到再从 earlySingletonObjects 里面获取,如果还获取不到,再尝试从 ingletonFactories 里面获取 beanName 对应的ObjectFactory,然后调用这个 ObjectFactory 的 getObject 来创建 bean,并放到 earlySingletonObjects 里面去,并且从 singletonFactories 里面 remove 掉这个 ObjectFactory。此时earlySingletonObjects中的 BeanA 就是早期曝光的 bean

  • 而对于后续的所有内存操作都只为了循环依赖检测时候使用,也就是在 allowEarlyReference 为 true 的情况下才会使用。

  • 这里涉及用于存储bean 的不同的 map,一开始可能很难理解,这也是所谓的三级缓存,用来处理循环依赖问题。

    • singletonObjects:用于保存 BeanName和创建bean 实例之间的关系,bean name --> bean instance(一级缓存
    • earlySingletonObjects:也是保存 BeanName 和创建 bean 实例之间的关系,与singletonObjects 的不同之处在于,当一个单例bean 被放到这里面后,那么当 bean 还在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用 (二级缓存
    • singletonFactories:用于保存 BeanName 和创建 bean的工厂之间的关系,bean name --> ObjectFactory (三级缓存
    • registeredSingletons:用来保存当前所有已注册的bean。

getObjectForBeanInstance

AbstractBeanFactory#getObjectFromFactoryBean根据方法名称可以知道,这个方法就是返回bean 实例对象的。我们无论是从缓存中获取到的 bean 还是通过不同的 scope 策略加载的 bean 都只是最原始的 bean 状态,并不一定是我们最终想要的 bean。举个例子,假如我们需要对工厂的 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean,而getObjectForBeanInstance就是完成这个工作的。

java 复制代码
 /**
    * 获取给定bean实例的对象,在FactoryBean的情况下,可以是bean实例本身,也可以是其创建的对象。这个话会有解释
    */
   protected Object getObjectForBeanInstance(
       Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
 ​
     // Don't let calling code try to dereference the factory if the bean isn't a factory.
     // 判断name是否是FactoryBean的 name
     if (BeanFactoryUtils.isFactoryDereference(name)) {
       if (beanInstance instanceof NullBean) {
         return beanInstance;
       }
       // 以&开头又不是FactoryBean实现类,则抛出异常
       if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
       }
       if (mbd != null) {
         mbd.isFactoryBean = true;
       }
       return beanInstance;
     }
 ​
     // Now we have the bean instance, which may be a normal bean or a FactoryBean.
     // If it's a FactoryBean, we use it to create a bean instance, unless the
     // caller actually wants a reference to the factory.
     // 此时bean可能是 FactoryBean 或者 普通的bean。
     // 判断如果 beanInstance 不是 FactoryBean而是普通的bean, 就直接返回
     if (!(beanInstance instanceof FactoryBean<?> factoryBean)) {
       return beanInstance;
     }
 ​
     // 到这一步就可以确定,当前beanInstance 是FactoryBean,并且需要获取getObject() 的结果
     Object object = null;
     if (mbd != null) {
       mbd.isFactoryBean = true;
     }
     else {
       // 尝试从缓存中加载bean。这一步是从 factoryBeanObjectCache 集合中获取
       // 在后面获取 bean 成功后,可能会将 其缓存到 factoryBeanObjectCache  中
       object = getCachedObjectForFactoryBean(beanName);
     }
     if (object == null) {
       // Return bean instance from factory.
       // Caches object obtained from FactoryBean if it is a singleton.
       // containsBeanDefinition 检测  beanDefinitionMap中也就是所有已经加载的类中检测是否定义beanName
       if (mbd == null && containsBeanDefinition(beanName)) {
         // 如果存在 则合并父类 bean 定义的属性
         mbd = getMergedLocalBeanDefinition(beanName);
       }
       boolean synthetic = (mbd != null && mbd.isSynthetic());
       // 这一步中对FactoryBean进行了解析。
       object = getObjectFromFactoryBean(factoryBean, beanName, !synthetic);
     }
     return object;
   }

获取给定bean实例的对象,在FactoryBean的情况下,可以是bean实例本身,也可以是其创建的对象。这句话怎么理解呢?比如下面这个例子

java 复制代码
 ​
 public class MyFactoryBean implements FactoryBean<MyObject> {
 ​
     @Override
     public MyObject getObject() throws Exception {
         // 这里创建并返回MyObject实例
         return new MyObject();
     }
 ​
     @Override
     public Class<?> getObjectType() {
         return MyObject.class;
     }
 ​
     @Override
     public boolean isSingleton() {
         return true;
     }
 }
 ​
 // 简单写个方法
 MyObject myObject = (MyObject) context.getBean("myFactoryBean");
 MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("&myFactoryBean");
 ​
  • 这也就为什么在一开始会有特殊字符处理,所以这个方法一开始在判断是否是FactoryBean
  • 如果不是FactoryBean那就是普通的 bean 实例,直接返回
  • 如果是FactoryBean,则使用这个FactoryBean创建 bean 实例
  • 调用getObjectFromFactoryBean获取 bean 实例

从上述的代码来看,其实这个方法没有什么重要的信息,大多是辅助代码以及一些功能性判断,而真正的核心代码委托给了FactoryBeanRegistrySupport#getObjectFromFactoryBean,我们一起来看看。

getObjectFromFactoryBean

java 复制代码
 protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 判断是否是单例并且是否已经缓存过了
    if (factory.isSingleton() && containsSingleton(beanName)) {
       synchronized (getSingletonMutex()) {
          // 获取缓存的单例对象
          Object object = this.factoryBeanObjectCache.get(beanName);
          if (object == null) {
             // 在这个方法中进行解析。调用 FactoryBean 的 getObject 方法。factory.getObject()
             object = doGetObjectFromFactoryBean(factory, beanName);
             // Only post-process and store if not put there already during getObject() call above
             // (e.g. because of circular reference processing triggered by custom getBean calls)
             // 因为是单例模式,所以要保证变量的全局唯一。所以这里如果缓存中已经创建好了bean则替换为已经创建好的bean
             Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
             if (alreadyThere != null) {
                object = alreadyThere;
             }
             else {
                // // 如果允许调用bean的后置处理器。因为这里是直接将bean创建返回了,如果要调用后置方法则只能在这里调用。
                if (shouldPostProcess) {
                   if (isSingletonCurrentlyInCreation(beanName)) {
                      // Temporarily return non-post-processed object, not storing it yet..
                      return object;
                   }
                   // 将beanName 添加到 singletonsCurrentlyInCreation 中缓存,表示当前bean正在创建中
                   beforeSingletonCreation(beanName);
                   try {
                      object = postProcessObjectFromFactoryBean(object, beanName);
                   }
                   catch (Throwable ex) {
                      throw new BeanCreationException(beanName,
                            "Post-processing of FactoryBean's singleton object failed", ex);
                   }
                   finally {
                      // 将beanName 从 singletonsCurrentlyInCreation 中移除,表示当前bean已经创建结束
                      afterSingletonCreation(beanName);
                   }
                }
                // 添加到factoryBeanObjectCache缓存中
                if (containsSingleton(beanName)) {
                   // 这里保存的是 beanName : FactoryBean
                   this.factoryBeanObjectCache.put(beanName, object);
                }
             }
          }
          return object;
       }
    }
    else {
       Object object = doGetObjectFromFactoryBean(factory, beanName);
       if (shouldPostProcess) {
          try {
             object = postProcessObjectFromFactoryBean(object, beanName);
          }
          catch (Throwable ex) {
             throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
          }
       }
       return object;
    }
 }

如果是单例并且在singletonObjects这个缓存中已经缓存过的,非单例获取未缓存过的直接使用doGetObjectFromFactoryBean,并调用后置处理器postProcessObjectFromFactoryBean

  • 先尝试在factoryBeanObjectCache这个缓存中获取,如未缓存
  • 则再从doGetObjectFromFactoryBean此方法使用factory.getObject()获取
  • 如果允许调用后置处理器,则调用postProcessObjectFromFactoryBean
  • 添加factoryBeanObjectCache缓存

我当时看到这里就有个疑问,为什么要先判断singletonObjects是否已经缓存了?那如果已经缓存了,为什么不直接使用这个 bean 实例呢?

  • singletonObjects这个缓存中存在对应的 bean,那么就意味着这个Bean已经被实例化过了。这并不意味着我们可以直接从singletonObjects缓存中获取Bean的实例对象。因为这里的 bean 只是一个原始的 bean 实例,也就是在这个方法解析之前所说,我们从缓存获取到的实例对象并不是最终状态的 bean 对象
  • 因为在Spring中,一个Bean的创建过程可能会涉及到一些后处理操作,这些操作可能会改变Bean的最终状态。这就是为什么我们需要尝试从factoryBeanObjectCache这个缓存中获取Bean的原因
  • factoryBeanObjectCache缓存中存储的是已经经过后处理的Bean实例。当我们调用getObjectFromFactoryBean方法时,如果beanName已经存在于singletonObjects缓存中,那么我们会尝试从factoryBeanObjectCache缓存中获取已经经过后处理的Bean实例。如果在factoryBeanObjectCache缓存中找不到,那么我们会执行后处理操作,然后将处理后的Bean实例存入factoryBeanObjectCache缓存
  • 这样做的好处是,我们可以确保无论何时获取Bean,都能获取到的是已经经过后处理的Bean实例,而不仅仅是原始的、未经处理的Bean实例。这对于保证Spring框架的稳定性和可预测性是非常重要的。

doGetObjectFromFactoryBean

经过上面的代码,还是没有看到有关键的信息,重要的操作委托给了FactoryBeanRegistrySupport#doGetObjectFromFactoryBean

而重要的代码就是这一行object = factory.getObject();

java 复制代码
 private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    Object object;
    try {
       object = factory.getObject();
    }
     ......
    return object;
 }

上面我们已经讲述了 FactoryBean 的调用方法,如果 bean 声明为 FactoryBean 类型,则当提取 bean 时提取的并不是 FactoryBean,而是 FactoryBean 中对应的getObject 方法返回的 bean,而 doGetObjectFromFactoryBean 正是实现这个功能的。但是,我们看到在上面的方法中除了调用 object = factory.getObject()得到我们想要的结果后并没有直接返回,而是接下来又做了些后处理的操作,回到getObjectFromFactoryBean继续跟踪进入 AbstractAutowireCapableBeanFactory类的 postProcessObjectFromFactoryBean 方法。

java 复制代码
 @Override
 protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
    return applyBeanPostProcessorsAfterInitialization(object, beanName);
 }
 ​
 @Override
 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
     throws BeansException {
 ​
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
     Object current = processor.postProcessAfterInitialization(result, beanName);
     if (current == null) {
       return result;
     }
     result = current;
   }
   return result;
 }
  • 对于后处理器的使用还未过多接触,后续会详细介绍,这里,我们只需了解在 Spring获取 bean 的规则中有这样一条:尽可能保证所有bean 初始化后都会调用注册的BeanPostProcessor 的 postProcessAfterInitialization 方法进行处理,在实际开发过程中大可以针对此特性设计自己的业务逻辑。

getParentBeanFactory

回到doGetBean方法,接下来解析getParentBeanFactory。如果存在父级工厂,并当前的BeanDefinition不存在,则尝试去父级工厂获取

java 复制代码
 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    // 递归到BeanFactory中检测
    String nameToLookup = originalBeanName(name);
    if (parentBeanFactory instanceof AbstractBeanFactory abf) {
       return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
       // Delegation to parent with explicit args.
       return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
       // No args -> delegate to standard getBean method.
       return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
       return (T) parentBeanFactory.getBean(nameToLookup);
    }
 }

getMergedLocalBeanDefinition

将当前的BeanDefinition与父级的BeanDefinition 合并成一个BeanDefinition,RootBeanDefinition:mdb,后面基本都是使用这个参数,一路点点点点下去,最终会来到AbstractBeanFactory#getMergedBeanDefinition

java 复制代码
 protected RootBeanDefinition getMergedBeanDefinition(
       String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
       throws BeanDefinitionStoreException {
 ​
    synchronized (this.mergedBeanDefinitions) {
       RootBeanDefinition mbd = null;
       RootBeanDefinition previous = null;
 ​
       // Check with full lock now in order to enforce the same merged instance.
       if (containingBd == null) {
          mbd = this.mergedBeanDefinitions.get(beanName);
       }
 ​
       if (mbd == null || mbd.stale) {
          previous = mbd;
          // 判断如果parentName为空则没必要进行合并了,直接克隆返回即可
          if (bd.getParentName() == null) {
             // Use copy of given root bean definition.
             if (bd instanceof RootBeanDefinition rootBeanDef) {
                mbd = rootBeanDef.cloneBeanDefinition();
             }
             else {
                mbd = new RootBeanDefinition(bd);
             }
          }
          else {
             // Child bean definition: needs to be merged with parent.
             BeanDefinition pbd;
             try {
                String parentBeanName = transformedBeanName(bd.getParentName());
                // 递归调用,解析更上层的parent BeanDefinition ,直到beanName=parentBeanName或者parentBeanName为 null
                if (!beanName.equals(parentBeanName)) {
                   pbd = getMergedBeanDefinition(parentBeanName);
                }
                else {
                   // 获取父bean的beanDefinition
                   if (getParentBeanFactory() instanceof ConfigurableBeanFactory parent) {
                      pbd = parent.getMergedBeanDefinition(parentBeanName);
                   }
                   else {
                      throw new NoSuchBeanDefinitionException(parentBeanName,
                            "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                  "': cannot be resolved without a ConfigurableBeanFactory parent");
                   }
                }
             }
             catch (NoSuchBeanDefinitionException ex) {
                throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                      "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
             }
             // Deep copy with overridden values.
             // 深拷贝
             mbd = new RootBeanDefinition(pbd);
             mbd.overrideFrom(bd);
          }
 ​
          // Set default singleton scope, if not configured before.
          if (!StringUtils.hasLength(mbd.getScope())) {
             mbd.setScope(SCOPE_SINGLETON);
          }
 ​
          // A bean contained in a non-singleton bean cannot be a singleton itself.
          // Let's correct this on the fly here, since this might be the result of
          // parent-child merging for the outer bean, in which case the original inner bean
          // definition will not have inherited the merged outer bean's singleton status.
          if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
             mbd.setScope(containingBd.getScope());
          }
 ​
          // Cache the merged bean definition for the time being
          // (it might still get re-merged later on in order to pick up metadata changes)
          if (containingBd == null && isCacheBeanMetadata()) {
             this.mergedBeanDefinitions.put(beanName, mbd);
          }
       }
       if (previous != null) {
          copyRelevantMergedBeanDefinitionCaches(previous, mbd);
       }
       return mbd;
    }
 }
  • 使用BeanDefinition获取parentName,判断如果parentName为空则没必要进行合并了,直接克隆返回即可
  • 如果存在父类的 bean,递归调用,解析更上层的parent BeanDefinition ,直到beanName=parentBeanName或者parentBeanName为 null
  • 获取父bean的beanDefinition#pbd,然后深拷贝到RootBeanDefinition#mdb
  • 添加缓存mergedBeanDefinitions

查询依赖 bean

原本以为这个方法很简单,但是当初详细深入进去,有几个地方有疑问的,带我一一剖析。

先直接看到源码中的isDependent这个方法。DefaultSingletonBeanRegistry#isDependent

java 复制代码
 private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    // 是否已经检测过了
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
       return false;
    }
    // 从aliasMap中获取别名映射的 beanName
    String canonicalName = canonicalName(beanName);
    // 获取依赖的beanName
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    if (dependentBeans == null || dependentBeans.isEmpty()) {
       return false;
    }
    // 如果 依赖于 beanName中存在 dependentBeanName 则说明存在循环依赖。
    // 代码走到这里说明是beanName 创建过程中要依赖 dependentBeanName。但是dependentBeans.contains(dependentBeanName) = true 则说明dependentBeanName依赖于beanName
    // 造成了 A依赖B,B依赖A的情况
    if (dependentBeans.contains(dependentBeanName)) {
       return true;
    }
    // 递归,确定没有A->B->C-A 这种长链路的循环依赖情况
    if (alreadySeen == null) {
       alreadySeen = new HashSet<>();
    }
    alreadySeen.add(beanName);
    for (String transitiveDependency : dependentBeans) {
       if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
          return true;
       }
    }
    return false;
 }

当时我的疑问? 问题1、为什么dependentBeans.contains(dependentBeanName)如果存在这个依赖就直接抛异常?(isDependent这个方法返回true,则 doGetCreate 方法就抛异常)

这里涉及到两个缓存dependentBeanMap、dependenciesForBeanMap,这两个缓存主要用于存储和管理Bean之间的依赖关系,但是作用一个是依赖的 bean 作为 key,一个是被依赖的作为 key。

如果一个 BeanA 使用 @Autowired 注入了 BeanB,那么在这两个缓存中的存储方式如下:

  • dependentBeanMap:在这个缓存中,BeanB 作为键,BeanA 作为值。这是因为 BeanA 依赖于 BeanB,所以 BeanB 被 BeanA 所依赖。
  • dependenciesForBeanMap:在这个缓存中,BeanA 作为键,BeanB 作为值。这是因为 BeanA 依赖于 BeanB,所以 BeanA 有一个依赖,即 BeanB。

所以一开始就确定beanName依赖于dependentBeanName,所以这里使用dependentBeanMap判断两者是否存在循坏依赖,如果存在,则直接抛异常

问题 2、为什么循坏依赖直接抛异常,不是有三级缓存吗?

这里有涉及到一个问题,mbd.getDependsOn();获取的并非是依赖注入(使用@Autowired)的依赖 bean,而是depends-on中的 bean,下面的beanA则依赖于beanB,但是beanB 并非直接注入到 beanA 中,而是要确定beanB的初始化必须要在beanA 之前

所以@Autowired是直接依赖注入,depends-on(@DependsOn)则是定义 bean 的初始化循序。

xml 复制代码
 <beans>
     <bean id="beanA" class="ExampleBean" depends-on="beanB">
         <property name="manager" ref="manager" />
     </bean>
     <bean id="beanB" class="BeanB" />
 </beans>
 ​

所以如果出现循坏依赖时,即 BeanA 与 BeanB 相互依赖,但是BeanA 又使用了@DependsOn依赖 BeanB,那么在初始化 BeanA 时,发现BeanB 要初始化完成在 BeanA 之前,但是 BeanB 又要依赖 BeanA,就会相互冲突,所以 spring 对此会直接抛异常。

注: @DependsOn:注解是在另外一个实例创建之后才创建当前实例,也就是,最终两个实例都会创建,只是顺序不一样 @ConditionalOnBean :注解是只有当另外一个实例存在时,才创建,否则不创建,也就是,最终有可能两个实例都创建了,有可能只创建了一个实例,也有可能一个实例都没创建

相关推荐
初晴~18 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
黑胡子大叔的小屋1 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark1 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
雷神乐乐2 小时前
Spring学习(一)——Sping-XML
java·学习·spring
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
V+zmm101343 小时前
基于小程序宿舍报修系统的设计与实现ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·ssm
文大。3 小时前
2024年广西职工职业技能大赛-Spring
java·spring·网络安全
一只小小翠3 小时前
EasyExcel 模板+公式填充
java·easyexcel
m0_748256344 小时前
QWebChannel实现与JS的交互
java·javascript·交互
Jelena技术达人4 小时前
Java爬虫获取1688关键字 item_search接口返回值详细解析
java·开发语言·爬虫