当前位置: 首页>后端>正文

Spring Bean的实例化

在SpringIOC容器初始化的时候,IOC容器中已经注册了BeanDefinition的信息,并放到了一个ConcurrentHashMap中,BeanDefinition接口中保存了类的class信息以及是否单例等信息。


Spring Bean的实例化,第1张

本文就是探究如何从已有的BeanDefinition信息,实例化出Bean对象。
根据上图就可以初步实现实例化Bean

  • 从beanDefinitionMap通过beanName获得BeanDefinition
  • 从BeanDefinition中获得beanClassName
  • 通过反射初始化beanClassName的实例instance
  • 构造函数从BeanDefinition的getConstructorArgumentValues()方法获取
  • 属性值从BeanDefinition的getPropertyValues()方法获取
  • 返回beanName的实例instance

1. BeanFactory中getBean的主体思路

以下是BeanFactory中重载的getBean方法:

// 根据bean的名字和Class类型等来得到bean实例    
Object getBean(String name) throws BeansException;    
Object getBean(String name, Class requiredType) throws BeansException;    
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

BeanFactory实现getBean方法在AbstractBeanFactory中,这个方法重载都是调用doGetBean方法进行实现的:

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

下面可以看一下doGetBean方法:

// 参数typeCheckOnly:bean实例是否包含一个类型检查
protected <T> T doGetBean(
            String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
            throws BeansException {

  // 解析bean的真正name,如果bean是工厂类,name前缀会加&,需要去掉
  String beanName = transformedBeanName(name);
  Object beanInstance;

  // Eagerly check singleton cache for manually registered singletons.
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    // 无参单例从缓存中获取
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }

  else {
    // 如果bean实例还在创建中,则直接抛出异常
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }

    // 如果 bean definition 存在于父的bean工厂中,委派给父Bean工厂获取
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // Not found -> check parent.
      String nameToLookup = originalBeanName(name);
      if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).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);
      }
    }

    if (!typeCheckOnly) {
      // 将当前bean实例放入alreadyCreated集合里,标识这个bean准备创建了
      markBeanAsCreated(beanName);
    }

    StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
        .tag("beanName", name);
    try {
      if (requiredType != null) {
        beanCreation.tag("beanType", requiredType::toString);
      }
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // 确保它的依赖也被初始化了.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
          }
          registerDependentBean(dep, beanName);
          try {
            getBean(dep); // 初始化它依赖的Bean
          }
          catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
          }
        }
      }

      // 创建Bean实例:单例
      if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
          try {
            // 真正创建bean的方法
            return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
          }
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      // 创建Bean实例:原型
      else if (mbd.isPrototype()) {
        // It's a prototype -> create a new instance.
        Object prototypeInstance = null;
        try {
          beforePrototypeCreation(beanName);
          prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
          afterPrototypeCreation(beanName);
        }
        beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }
      // 创建Bean实例:根据bean的scope创建
      else {
        String scopeName = mbd.getScope();
        if (!StringUtils.hasLength(scopeName)) {
          throw new IllegalStateException("No scope name defined for bean ′" + beanName + "'");
        }
        Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
          Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
              return createBean(beanName, mbd, args);
            }
            finally {
              afterPrototypeCreation(beanName);
            }
          });
          beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
        }
        catch (IllegalStateException ex) {
          throw new ScopeNotActiveException(beanName, scopeName, ex);
        }
      }
    }
    catch (BeansException ex) {
      beanCreation.tag("exception", ex.getClass().toString());
      beanCreation.tag("message", String.valueOf(ex.getMessage()));
      cleanupAfterBeanCreationFailure(beanName);
      throw ex;
    }
    finally {
      beanCreation.end();
    }
  }

  return adaptBeanInstance(name, beanInstance, requiredType);
}

主要的几点:

  • 解析bean的真正name,如果bean是工厂类,name前缀会加&,需要去掉
  • 无参单例先从缓存中尝试获取
  • 如果bean实例还在创建中,则直接抛出异常
  • 如果bean definition 存在于父的bean工厂中,委派给父Bean工厂获取
  • 标记这个beanName的实例正在创建
  • 确保它的依赖也被初始化
  • 真正创建
    • 单例时
    • 原型时
    • 根据bean的scope创建

1.2 循环依赖问题的类型

主要有三种情况:

  1. 通过构造器进行依赖注入时产生的循环依赖问题。(在new对象的时候就会阻塞住,A依赖B,B依赖A,当创建A实例时,发现依赖了B,那么就会实例化B,但是实例化B的时候又依赖了A,这个时候又去实例化A,这样就一直处于循环创建对象的过程)
  2. 通过setter方法进行依赖注入且是多例(原型)模式下产生的循环依赖问题。(每一次调用getBean的时候,都会产生一个新的Bean,如此反复就会有无穷无尽的Bean产生,最后导致OOM的问题)
  3. 通过setter方法进行依赖注入且实在单例模式下产生的循环依赖问题(Spring自动解决)

1.3 Spring如何解决单例模式下的循环依赖问题

Spring采用了三级缓存的方式自动为我们解决单例模式下循环依赖的问题(三个缓存之间是互斥的,不会针对同一个Bean的实例同时存储,调用getBean需要从三个缓存中依次获取)

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

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

一级缓存(singletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象;该缓存是对外使用的(程序员)。
二级缓存(earlySingletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是半成品对象;(Bean还在创建中,但是被提前暴露了引用)
三级缓存(singletonFactories):单例工厂的缓存;通过ObjectFactory对象存储单例模式下提前暴露的Bean实例的引用,ObjectFactory持有被提前暴露Bean的引用

Spring Bean的实例化,第2张
ObjectFactory.png

下面是实现三级缓存的方法:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // Spring首先从singletonObjects(一级缓存)中尝试获取
  Object singletonObject = this.singletonObjects.get(beanName);
  // 若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取
  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) {
            //若是仍是获取不到而且容许从singletonFactories经过getObject获取,则经过singletonFactory.getObject()(三级缓存)获取
              singletonObject = singletonFactory.getObject();
              //若是获取到了则将singletonObject放入到earlySingletonObjects,也就是将三级缓存提高到二级缓存中
              this.earlySingletonObjects.put(beanName, singletonObject);
              this.singletonFactories.remove(beanName);
          }
        }
    }
  }
  return (singletonObject != NULL_OBJECT singletonObject : null);
}

其中使用到的两个重要的方法:

  • isSingletonCurrentlyInCreation():判断当前单例bean是否正在建立中,也就是没有初始化完成(好比A的构造器依赖了B对象因此得先去建立B对象, 或则在A的populateBean过程当中依赖了B对象,得先去建立B对象,这时的A就是处于建立中的状态。)
  • allowEarlyReference :是否容许从singletonFactories中经过getObject拿到对象。

1.4 为什么第三级缓存要使用ObjectFactory?

如果仅仅是为了解决循环依赖,使用二级缓存就可以了,但是如果对象实现了AOP,那么最终注入到其他Bean的时候,并不是最终的代理对象,而是原始的,这是就需要通过三级缓存的ObjectFactory才能提前产生最终需要的代理对象。

Spring Bean的实例化,第3张

什么时候将Bean的引用提前暴露给第三级缓存的ObjectFactory持有?
时机就是在第一步实例化之后,第二步依赖注入之前,完成此操作。
Spring Bean的实例化,第4张

1.5 解决构造函数相互注入造成的循环依赖

对于通过构造方法进行依赖注入时产生的循环依赖问题没办法自动解决,针对这种情况,可以使用@Lazy注解来解决。

也就是说,对于类A和类B都是通过构造器注入的情况,可以在A或者B的构造函数的形参上加个@Lazy注解实现延迟加载。@Lazy实现原理是,当实例化对象时,如果发现参数或者属性有@Lazy注解修饰,那么就不直接创建所依赖的对象了,而是使用动态代理创建一个代理类。

比如,类A的创建:A a=new A(B),需要依赖对象B,发现构造函数的形参上有@Lazy注解,那么就不直接创建B了,而是使用动态代理创建了一个代理类B1,此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A。但因为在注入依赖时,类A并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。这个时候就可以从一级缓存中获取到懒加载的对象。


https://www.xamrdz.com/backend/32d1943126.html

相关文章: