【修炼内功】[spring-framework] [3] Bean是如何被创建的

本文已收录【修炼内功】跃迁之路

spring-framework.jpg

林中小舍.png

书接上文,在 BeanDefinitionReader 一文中简单介绍了XMLBeanFactory解析xml配置、并注册BeanDefinition的逻辑,本文就bean的实例化过程及销毁做简要分析

先放一张大图(点击图片放大查看,右键或长按保存后更清晰),展示完整的bean创建过程及销毁过程,如果对spring原理有一些理解可将此图作为开发过程中的参考,如果对spring原理还有一些模糊可继续向下阅读(长文预警!)

SpringBeanFactory

BeanDefinition

通过前文简单了解到,Spring在初始化过程中并不是直接实例化bean,而是先收集所有bean的元数据信息并注册,bean的元数据描述为接口BeanDefinition,该接口定义了你能想到的一切有关bean的属性信息

BeanDefinition衍生出一系列实现类

Beans.BeanDefinition

  • AbstractBeanDefinition

    如同其他Spring类,大部分BeanDefinition接口的逻辑都由该抽象类实现

  • GenericBeanDefinition

    GenericBeanDefinition是一站式、用户可见的bean definition,如何理解“用户可见”?

    可见的bean definition意味着可以在该bean definition上定义post-processor来对bean进行操作

  • RootBeanDefinition

    当bean definition存在父子关系的时候,RootBeanDefinition用来承载父元数据的角色(也可独立存在),同时它也作为一个可合并的bean definition使用,在Spring初始化阶段,所有的bean definition均会被(向父级)合并为RootBeanDefinition,子bean definition(GenericBeanDefinition/ChildBeanDefinition)中的定义会覆盖其父bean definition(由parentName指定)的定义

  • ChildBeanDefinition

    当bean definition存在父子关系的时候,ChildBeanDefinition用来承载子元数据的角色(也可独立存在),在Spring推出GenericBeanDefinition后,其完全可以被GenericBeanDefinition替代,目前使用场景已经非常少

  • AnnotatedBeanDefinition

    如其名,主要用来定义注解场景的bean definition

    • ScannedGenericBeanDefinition

      主要用来定义@Component@Service等bean definition,其AnnotationMetadata metadata属性用来存储该bean的类注解信息

    • AnnotatedGenericBeanDefinition

      ScannedGenericBeanDefinition不同的是,其主要用来定义@Configuration等配置类中@Bean的bean definition,其AnnotationMetadata metadata属性与ScannedGenericBeanDefinition相同,MethodMetadata factoryMethodMetadata属性用来存储@Bean描述的方法信息

BeanDefinitionHolder只是简单捆绑了BeanDefinition、bean-name、bean-alias,用于注册BeanDefinition及别名alias

BeanRegistry

在一般工程中,bean的定义分散在各种地方(尤其使用注解之后),Spring并不能在解析出每一个bean的元数据信息后立即对该bean做实例化动作,对于依赖的分析与注入、类(方法)的代理、功能上的扩展等,必须等所有的bean元数据全部解析完成之后才能进行

在bean元数据解析完成之后、bean实例化之前,对bean的元数据信息有一个暂存的过程,这个过程便是bean的注册

bean的注册逻辑分两步,一为BeanDefinition的注册,一为别名的注册

  • BeanDefinition注册的定义在BeanDefinitionRegistry#registerBeanDefinition,其实现使用一个Map<String, BeanDefinition>来保存bean-name和BeanDefinition的关系
  • 别名的注册定义在AliasRegistry#registerAlias,其实现同样使用一个Map<String, String>来保存别名alias-name和bean-name(或另一个别名alias-name)的关系

在完成bean的元数据注册之后,便是根据详尽的元数据信息进行实例化了

BeanFactory

bean的实例化过程比较复杂(Spring考虑到了各种场景),附上BeanRegistry&BeanFactory相关的类依赖图

Beans.ListableBeanFactory

初看此图,请不要失去信心和耐心,图中各类的作用会一一讲解(见 #附录#相关类说明),这里先介绍几个核心的接口

  • AliasRegistry

    bean别名注册和管理

  • BeanDefinitionRegistry

    bean元数据注册和管理

  • SingletonBeanRegistry

    单例bean注册和管理

  • BeanFactory

    bean工厂,提供各种bean的获取及判断方法

大致浏览上图(类依赖图)不难发现,核心实现落在了DefaultListableBeanFactory,我们以此类作为切入点分析bean实例化过程

bean的实例化过程发生在getBean调用阶段(对于singleton则发生在首次调用阶段),getBean的实现方法众多,我们追根溯源,找到最通用的方法AbstractBeanFactory#doGetBean

// org.springframework.beans.factory.support.AbstractBeanFactory
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    // 1. 获取真正的beanName
    final String beanName = transformedBeanName(name);
    Object bean;

    // 2. 尝试获取(提前曝光的)singleton bean实例(为了解决循环依赖)
    Object sharedInstance = getSingleton(beanName);
    
    // 3. 如果存在
    if (sharedInstance != null && args == null) { ... }
    
    // 4. 如果不存在
    else { ... }
    
    // 5. 尝试类型转换
    if (requiredType != null && !requiredType.isInstance(bean)) { ... }
    
    return (T) bean;
}

bean的实例化过程虽然复杂,但大体逻辑非常清楚

Flowchart.doGetBean

接下,就以上五个子流程(蓝色部分)一一展开

在实例化bean的过程当中,Spring会使用大量的中间态来判断、处理各种场景和情况,此处先行列出Spring所使用的一些关键的中间态(各中间态的作用会在下文介绍,见 #附录#中间态说明),以便在下文中更好地理解bean实例化过程中对各种情况的判断和处理逻辑

Spring.Bean.Cache

bean name转换

在使用bean-name获取bean的时候,除了可以使用原始bean-name之外,还可以使用alias别名等,bean-name的转换则是将传入的‘bean-name’一层层转为最原始的bean-name

Return the bean name, stripping out the factory dereference prefix if necessary, and resolving aliases to canonical names.
protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

函数canonicalName的作用则是利用别名注册aliasMap,将别名alias转为原始bean-name

函数transformedBeanName比较特殊,其是将FactoryBean的bean-name前缀 '&' 去除(BeanFactory#FACTORY_BEAN_PREFIX 下文会介绍)

Flowchart.transformedBeanName

尝试获取单例

拿到原始的bean-name之后,便可以实例化bean或者直接获取已经实例化的singleton-bean,此处为什么叫‘尝试’获取呢?

在获取singleton-bean的时候一般存在三种情况:1. 还未实例化(或者不是单例);2. 已经实例化;3. 正在实例化;

  • 对于 “1. 还未实例化” ,返回null即可,后续进行实例化动作
  • 对于“2. 已经实例化”,直接返回实例化的singleton-bean
  • 对于“3. 正在实例化”,则较难理解

Spring中对于singleton-bean,有一个sharedInstance的概念,在调用getSingleton函数时,返回的不一定是完全实例化的singleton-bean,有可能是一个中间状态(创建完成,但未进行属性依赖注入及其他后处理逻辑),这种中间状态会通过getSingleton函数提前曝光出来,目的是为了解决循环依赖(下文会详细介绍循环依赖)

Beans.CircularReferences

在实例化beanA的过程中,需要依赖beanBbeanC,如果beanC同时又依赖beanA,则需要beanA在实例化完成之前提前曝光出来,以免造成beanA等待beanC实例化完成,beanC等待beanA实例化完成,类似一种死锁的状态

在继续进行之前,有必要简单介绍几个中间态(详见 #附录#中间态说明

  • singletonObjects

    缓存已经实例化完成的singleton-bean

  • earlySingletonObjects

    缓存正在实例化的、提前曝光的singleton-bean,用于处理循环依赖

  • singletonFactories

    缓存用于生成earlySingletonObjectObjectFactory

ObjectFactory,定义了一个用于创建、生成对象实例的工厂方法

@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

介绍了上述之后,再来描述getSingleton的逻辑就会比较清楚

Flowchart.getSingleton

不用纠结上述中间态的值是何时被设置进去的,下文会逐步提及

FactoryBean处理(sharedInstance存在的逻辑)

上述 sharedInstance 一定是我们需要的bean实例么?未必!

定义bean的时候可以通过实现FactoryBean接口来定制bean实例化的逻辑,以此增加更多的灵活性及可能性(How to use the Spring FactoryBean?

@Bean(initMethod = "init", destroyMethod = "destroy")
public FactoryBean myBean() {
    return new FactoryBean<MyBean>() {
        /**
         * 定制bean初始化逻辑
         */
        @Override
        public MyBean getObject() throws Exception {
            MyBean myBean = new MyBean();
            // ... 定制化的逻辑
            return myBean;
        }

        /**
         * 真正的bean类型
         */
        @Override
        public Class<?> getObjectType() {
            return MyBean.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }
    }
}

通过注册FactoryBean类型的bean,实例化后的原始实例类型同样为FactoryBean,但我们需要的是通过FactoryBean#getObject方法得到的实例,这便需要针对FactoryBean做一些处理,这也是接下来要讲解的函数AbstractBeanFactory#getObjectForBeanInstance

Get the object for the given bean instance, either the bean instance itself or its created object in case of a FactoryBean.

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.

该函数要实现的逻辑比较简单,如果sharedInstanceFactoryBean,则使用getObject方法创建真正的实例

getObjectForBeanInstance是一个通用函数,并不只针对通过getSingleton得到的sharedInstance,任何通过缓存或者创建得到的 rawInstance,都需要经过getObjectForBeanInstance处理,拿到真正需要的 beanInstance

简单介绍getObjectForBeanInstance函数的入参

/**
 * @param beanInstance  sharedInstance / rawInstance,可能为FactoryBean
 * @param name            传入的未做转换的 bean name
 * @param beanName        对name做过转换后的原始 canonical bean name
 * @param mbd            合并后的RootBeanDefinition,下文会介绍
 */
protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, RootBeanDefinition mbd)

getObjectForBeanInstance函数的处理逻辑

Flowchart.getObjectForBeanInstance

上图中有一个逻辑判断,如果入参name以'&' (BeanFactory#FACTORY_BEAN_PREFIX)开头则直接返回(BeanFactory)

这里兼容了一种情况,如果需要获取/注入FactoryBean而不是getObject生成的实例,则需要在bean-name/alias-name前加入'&'

/**
 * 注入FactoryBean#getObject生成的实例
 */
@Autowired
private MyBean myBean;

/**
 * 直接注入FactoryBean
 */
@Resource(name = "&myBean")
private FactoryBean<MyBean> myFactoryBean;

对于singleton,FactoryBean#getObject的结果会被缓存到factoryBeanObjectCache,对于缓存中不存在或者不是singleton的情况,会通过FactoryBean#getObject生成(上图中蓝色未展开的逻辑)

Spring并非简单的调用FactoryBean#getObject,而是分为两部分处理

bean instance生成

上图中doGetObjectFromFactoryBean,主要对getObject方法进行了包装,判断是否需要在SecurityManager框架内执行以及对null结果进行封装(NullBean)

Flowchart.doGetObjectFromFactoryBean

bean instance后处理

上图中postProcessObjectFromFactoryBean,主要对生成的bean instance做一些后处理(可以跟踪到AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization),

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 拿到所有注册的BeanPostProcessor,执行后处理动作
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

postProcessAfterInitialization函数可以对现有bean instance做进一步的处理,甚至可以返回新的bean instance,这就为bean的增强提供了一个非常方便的扩展方式(可以思考一下,AOP的代理类是如何生成的)

加载bean实例(sharedInstance不存在的逻辑)

以上,讨论了bean instance存在于缓存中的情况,如果缓存中不存则需要进行bean的加载

Flowchart.createBeanOverview

简单来讲,bean的加载/创建分为三大部分

  1. 将BeanDefinition合并为RootBeanDefinition

    这里类似类继承,子BeanDefinition属性会覆盖父BeanDefinition

  2. 依次加载所依赖的bean

    对于有依赖的情况,优先递归加载依赖的bean

  3. 按照不同的bean类型,根据BeanDefinition的定义进行加载/创建

BeanDefinition合并(RootBeanDefinition)

将BeanDefinition转为RootBeanDefinition,如果存在父子关系,则进行合并

这里不再赘述,可以参考 AbstractBeanFactory#getMergedLocalBeanDefinition

加载depends-on beans

String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
    // 遍历所有的依赖
    for (String dep : dependsOn) {
        // 检测循环依赖
        if (isDependent(beanName, dep)) { /* throw exception */ }
        // 注册依赖关系
        registerDependentBean(dep, beanName);
        // 递归getBean,加载依赖bean
        try { getBean(dep); }
        catch (NoSuchBeanDefinitionException ex) { /* throw exception */ }
    }
}

逻辑很简单,但这里涉及到两个中间态dependentBeanMap、dependenciesForBeanMap

  • dependentBeanMap

    存储哪些bean依赖了我(哪些bean里注入了我)

    如果 beanB -> beanA, beanC -> beanA,key为beanA,value为[beanB, beanC]

  • dependenciesForBeanMap

    存储我依赖了哪些bean(我注入了哪些bean)

    如果 beanA -> beanB, beanA -> beanC,key为beanA,value为[beanB, beanC]

理解两者的存储关系,有助于在阅读源码的过程中理解bean的加载和销毁顺序

加载singleton bean实例

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

这里涉及两个比较核心的函数createBeangetObjectForBeanInstance

  • createBean

    根据BeanDefinition的内容,创建/初始化bean instance

  • getObjectForBeanInstance

    上文已经介绍过,主要处理FactoryBean,将FactoryBean转为真正需要的bean instance

createBean被包装在lambda(singletonFactory)中作为getSingleton的参数,我们来看getSingleton的实现逻辑

Flowchart.createSingleton

所以,关键的逻辑在createBean函数中,bean的创建逻辑较为复杂,我们放到后面介绍

加载prototype bean实例

else if (mbd.isPrototype()) {
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally { afterPrototypeCreation(beanName);    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

prototype bean的创建与singleton bean类似,只是不会缓存创建完成的bean

Flowchart.createPrototype

加载其他scope bean实例

scope,即作用域,或者可以理解为生命周期

上文介绍了singleton-bean及prototype-bean的创建过程,严格意义上讲以上两种都是一种特殊的scope-bean,分别对应ConfigurableBeanFactory#SCOPE_SINGLETONConfigurableBeanFactory#SCOPE_PROTOTYPE,前者作用域为整个IOC容器,也可理解为单例,后者作用域为所注入的bean,每次注入(每次触发getBean)都会重新生成

Spring中还提供很多其他的scope,如WebApplicationContext#SCOPE_REQUESTWebApplicationContext#SCOPE_SESSION,前者作用域为一次web request,后者作用域为一个web session周期

自定义scope的bean实例创建过程与singleton bean的创建过程十分相似,需要实现Scopeget方法(org.springframework.beans.factory.config.Scope#get),

else {
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
    if (scope == null) { /* throw exception */ }
    try {
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            // createBean被封装在Scope#get函数的lambda参数ObjectFactory中
            try { return createBean(beanName, mbd, args); }
            finally { afterPrototypeCreation(beanName); }
        });
        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    } catch (IllegalStateException ex) { /* throw exception */}
}

Scope接口除了get方法之外,还有一个remove方法,前者用于定义bean的初始化逻辑,后者用于定义bean的销毁逻辑

public interface Scope {
  /**
   * Return the object with the given name from the underlying scope
   */
  Object get(String name, ObjectFactory<?> objectFactory);
  
   /**
   * Remove the object with the given name from the underlying scope.
   */
  Object remove(String name);
}

WebApplicationContext#SCOPE_SESSION对应的Scope实现见org.springframework.web.context.request.SessionScope

WebApplicationContext#SCOPE_REQUEST对应的Scope实现见org.springframework.web.context.request.RequestScope

以上两种Scope实现都较为简单,前者将初始化的bean存储在request attribute种,后者将初始化的bean存储在http session中,具体细节请自行查阅源码

Q: Spring中实现了哪些Scope?又是什么时候注册的?

Bean创建过程

AbstractAutowireCapableBeanFactory#createBean

了解bean创建的过程也是一个抽丝剥茧的过程,真正创建的过程封装在AbstractAutowireCapableBeanFactory#doCreateBean中,而在此之前有一些准备工作,整体流程如下图

Flowchart.createBean

  1. resolveBeanClass

    这一步骤用于锁定bean class,在没有显示指定beanClass的情况下,使用className加载beanClass

  2. 验证method overrides

    在 [[spring-framework] [2] BeanDefinitionReader](https://segmentfault.com/a/11... 一文中有提到过lookup-method及replace-method,该步骤是为了确认以上两种配置中的method是否存在

  3. 执行InstantiationAwareBeanPostProcessor前处理器(postProcessBeforeInstantiation)

    这里要特别注意的是,如果这个步骤中生成了“代理”bean instance,则会有一个短路操作,直接返回该bean instance而不再执行doCreate,这是一个“细思极恐”的操作,但在一些特殊场景(尤其框架之中)提供了良好的扩展机制

    try {
        // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
              // 如果这里生成了代理的bean instance会直接返回
            return bean;
        }
    } cache (Throwable ex) { // throw exception }
    
    try {
      // 创建bean instance
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      // ...
    }
    Q: InstantiationAwareBeanPostProcessor的使用场景有哪些?Spring有哪些功能使用了InstantiationAwareBeanPostProcessor?它们是在什么时候注册的?
  4. doCreateBean (AbstractAutowireCapableBeanFactory)

    真正bean的创建及初始化过程在此处实现,但Spring对bean创建及初始化逻辑的复杂程度完全超出了本篇文章之承载,这里只对一些关键的逻辑做梳理

    Flowchart.doGetBean

创建bean实体

AbstractAutowireCapableBeanFactory#createBeanInstance

Flowchart.createBeanInstance

从上面的流程图可以看出,创建bean实体不一定会使用到构造函数,有两个特殊的方式

1. instance supplier

AbstractAutowireCapableBeanFactory#obtainFromSupplier

从Spring5开始,多了一种以函数式注册bean的方式(参考https://www.baeldung.com/spri...

// 注册MyService
context.registerBean(MyService.class, () -> new MyService());
// 注册MyService,并指定bean name
context.registerBean("mySecondService", MyService.class, () -> {
    MyService myService = new MyService();
    // 其他的逻辑
    return myService;
});
// 注册MyService,指定bean name,并增强bean definition
context.registerBean("myCallbackService", MyService.class, () -> {
    MyService myService = new MyService();
    // 其他的逻辑
    return myService;
}), bd -> bd.setAutowireCandidate(false));

或者

// 构建BeanDefinition
BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MyService.class, () -> {
    MyService myService = new MyService();
    // 其他的逻辑
    return myService;
});
// 注册BeanDefinition
beanFactory.registerBeanDefinition("myService", bd);

通过以上方式注册的bean,Spring会调用该supplier生成bean实体

2. factory method

AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod

ConstructorResolver#instantiateUsingFactoryMethod

在xml配置中还有一种不太常见的bean注册方式

public class MyHome {
  // 静态方法
  public static MyHome create() {
    return new MyHome();
  }
}

public class MyFactory {
  // 非静态方法
  public MyHome create() {
    return new MyHome();
  }
}
<bean id="myFactory" class="com.manerfan.MyFactory"></bean>

<!-- 方式一 -->
<!-- 使用指定类的静态方法 -->
<bean id="myHome1" 
      class="com.manerfan.MyHome" 
      factory-method="create"></bean>  
<!-- 方式二 -->
<!-- 使用指定bean的非静态方法 -->
<bean id="myHome2" 
      class="com.manerfan.MyHome" 
      factory-method="create" 
      factory-bean="myFactory"></bean>  

Spring会通过指定类的指定方法生成bean实体,其中有两种方式,一种方式(仅指定factory-method)使用指定类的静态方法生成,另一种方式(同时指定factory-methodfactory-bean)使用指定bean的非静态方法生成

同时factory-method中还允许传入一些参数,如果存在同名函数,Spring会根据参数的个数及类型找到匹配的method

public class MyHome {
  private MyHouse house;
  private MyCar car;
  // setters
}

public class MyFactory {
  // 携带入参
  public MyHome create(MyHouse house, MyCar car) {
     MyHome myHome = new MyHome();
    myHome.setHouse(house);
    myHome.setCar(car)
    return myHome;
  }
  
  // 同名函数
  public MyHome create(MyHouse house) {
     MyHome myHome = new MyHome();
    myHome.setHouse(house);
    myHome.setCar(defaultCar)
    return myHome;
  }
}
<bean id="myHome2" 
      class="com.manerfan.MyHome" 
      factory-method="create" 
      factory-bean="myFactory">
  <!-- 这里使用的是构造函数参数类型 -->
  <constructor-arg name="house" ref="house"/>
  <constructor-arg name="car" ref="car"/>
</bean>  

这样的代码是不是让你想到了@Configuration中的@Bean,@Bean所修饰方法如果存在参数的话,Spring会通过参数的类型及名称自动进行依赖注入

@Configuration
public class MyFactory {
  @Bean
  public MyHome create(MyHouse house, MyCar car) {
    MyHome myHome = new MyHome();
    myHome.setHouse(house);
    myHome.setCar(car)
    return myHome;
  }
}

我们可以大胆猜测,@Configuration + @Bean的实现方式就是factory-bean + factory-method,在后文介绍Spring的注解体系时会揭晓

使用指定(类)bean的(静态)方法创建bean实体的逻辑在ConstructorResolver#instantiate(String, RootBeanDefinition, Object, Method, args),而真正的逻辑在SimpleInstantiationStrategy#instantiate(RootBeanDefinition, String, BeanFactory, Object, Method, Object...),其核心的执行逻辑非常简单,有了方法factoryMethod(factoryBean)及入参args,便可以调用该方法创建bean实体

Object result = factoryMethod.invoke(factoryBean, args);

factoryBean可以通过beanFactory.getBean获取到(正是当前在讲的逻辑),factoryMethod可以通过反射获取到,入参args如何获取?这便涉及到Spring中的一个重要概念 -- 依赖注入,如何准确的找到依赖的实体并将其注入,便是接下来的重点,这里涉及到一个非常重要的函数ConstructorResolver#resolvePreparedArguments,该函数的作用是将BeanDefinition中定义的入参转换为真是需要的参数(xml中定义的或者注解中定义的),在 BeanDefinitionReader 一文中有过介绍,ref会被封装为RuntimeBeanReference存储、value会被封装为TypedStringValue存储等等,如何将这些封装好的存储类型转为真正需要的函数参数,便是ConstructorResolver#resolvePreparedArguments函数的作用

Flowchart.resolvePreparedArguments

这里分成了三大分支

  • resolveValueIfNecessary

    针对BeanMetadataElement,进行值的转换,其中又会包含特别细的分支,大致如下

    • RuntimeBeanNameReference

      AbstractBeanFactory#evaluateBeanDefinitionString

      支持Spl表达式解析bean name

    • BeanDefinitionHolder 、BeanDefinition

      BeanDefinitionValueResolver#resolveInnerBean

      与createBean函数的逻辑类似,创建一个inner bean

    • DependencyDescriptor

      AutowireCapableBeanFactory#resolveDependency

      用来处理OptionalBean、LazyBean、AutowireCandidateBean等(详见下文“注解注入”一节)

    • ManagedArray、ManagedList、ManagedSet、ManagedMap

      BeanDefinitionValueResolver#resolveManagedArray

      BeanDefinitionValueResolver#resolveManagedList

      BeanDefinitionValueResolver#resolveManagedSet

      BeanDefinitionValueResolver#resolveManagedMap

      内部递归使用resolveValueIfNecessary方法获取bean并最终封装成对应的类型

    • ManagedProperties

      通过BeanDefinitionValueResolver#evaluate(Spel)计算value的值,最终封装为Properties

    • TypedStringValue

      通过BeanDefinitionValueResolver#evaluate(Spel)计算value的值

对于这部分内容,Spring在接下来的发展中可能还会不断地扩充

  • String

    AbstractBeanFactory#evaluateBeanDefinitionString

    与resolveValueIfNecessary中的RuntimeBeanNameReference一致,支持Spl表达式解析表达式

  • 其他

    • InjectionPoint

      使用ThreadLocal提供当前bean被注入到的注入点,可以参考 https://www.baeldung.com/what...

      @Bean
      @Scope("prototype")
      public Logger logger(InjectionPoint injectionPoint) {
          // return MyComponent.class
          return Logger.getLogger(injectionPoint.getMethodParameter().getContainingClass());
      }
      
      @Component
      public class MyComponent {
        @Autowired
        private Logger logger;
      }
    • 其他

      与resolveValueIfNecessary中的DependencyDescriptor一致,用来处理OptionalBean、LazyBean等

需要留意的是,上述在进行依赖注入的过程中,都会调用DefaultSingletonBeanRegistry#registerDependentBean方法,将各bean之间的依赖关系保存起来,如同前文在介绍加载depends-on时一致,bean之间的依赖关系会分别存放在dependentBeanMapdependenciesForBeanMap之中(下文在介绍属性注入的时候亦是如此),这对于bean销毁顺序的理解起着至关重要的作用

在返回之前还有convertIfNecessary的方法调用,该函数是将上述解析得到的值转换为函数参数真正的类型

为何要转换?其实上述过程拿到的值并非真正需要的值,如

public class MyComponent {
  private Resource initSql;
}
<bean id="myComponent" class="com.manerfan.MyComponent">
  <property name="initSql" value="classpath:/init/sql/init.sql"></property>
</bean>

或者

public class MyComponent {
  @Value("${init.sql}")
  // or @Value("classpath:/init/sql/init.sql")
  private Resource initSql;
}
init.sql=classpath:/init/sql/init.sql

不论哪种形式,在convertIfNecessary之前解析到的值都是字符串 "classpath:/init/sql/init.sql",convertIfNecessary的作用便是将上述解析得到的值转换为函数参数真正的类型Resource

convert的逻辑在TypeConverterDelegate#convertIfNecessary,其内部基本的逻辑为

  1. 找到合适的PropertyEditor (propertyEditorRegistry)
  2. 使用PropertyEditor的setValue/getValue方法进行转换

url转换为ResourcePropertyEditor对应为 org.springframework.core.io.ResourceEditor,正是使用ResourceEditor将 字符串"classpath:/init/sql/init.sql”转为对应的Resource

Q: Spring默认的PropertyEditor有哪些?又是什么时候注册的?

3. 有参构造函数

AbstractAutowireCapableBeanFactory#autowireConstructor

ConstructorResolver#autowireConstructor

使用有参构造函数创建bean实例的一个点在于寻找与参数相对应的构造函数(可能定义了多个构造函数),而对于参数的解析和转换(参数的依赖注入)则与使用factory method一样,调用ConstructorResolver#resolvePreparedArguments函数进行处理,这里不再重复描述

在拿到真实的入参及对应的构造函数后,下一步便是使用构造函数来创建bean实例,但事情貌似也并没有那么简单

实例化的过程在ConstructorResolver#instantiate,内部并没有统一利用反射技术直接使用构造函数创建,而是分为两种情况

Flowchart.instantiate

一种,没有设置override-method时,直接使用构造函数创建

一种,在设置了override-method时,使用cglib技术构造代理类,并代理override方法

以上,Spring默认的实例化策略为CglibSubclassingInstantiationStrategy

4. 无参构造函数

AbstractAutowireCapableBeanFactory#instantiateBean

无参构造函数创建bean实例的过程与有参构造函数创建过程完全一致,只是少了参数的依赖注入,使用默认无参构造函数进行实例化

BeanDefinition后处理

AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors

在属性注入之前提供一次机会来对BeanDefinition进行处理,内部执行所有注册MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition方法

在阅读源码时注意到一个MergedBeanDefinitionPostProcessor的实现类 AutowiredAnnotationBeanPostProcessor,深入到实现内部AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata,其实现了两个注解类的解析 @Value@Autowired ,找到注解修饰的Filed或者Method并缓存,具体的逻辑会在属性注入一节中详细介绍

Q: Spring注册了哪些MergedBeanDefinitionPostProcessor?它们都是做什么用的?又是什么时候注册的?

提前暴露实体

DefaultSingletonBeanRegistry#addSingletonFactory -> AbstractAutowireCapableBeanFactory#getEarlyBeanReference

还记得上文介绍的“尝试获取单例”(AbstractBeanFactory.getSingleton)么?为了解决循环依赖会将singleton-bean提前暴露出来,暴露的逻辑会封装为ObjectFactory(AbstractAutowireCapableBeanFactory#getEarlyBeanReference实现)缓存在DefaultSingletonBeanRegistry.singletonFactories中,在getBean的逻辑getSingleton中会执行ObjectFactory的逻辑将singleton提前暴露

此时暴露的singleton-bean仅完成了bean的实例化,属性注入、初始化等逻辑均暂未执行

属性注入

AbstractAutowireCapableBeanFactory#populateBean

在“创建bean实体”小节中介绍了factory method方式及有参构造函数方式的参数注入逻辑,除此之外还有一种注入便是属性注入

Flowchart.populateBean

流程图中两次出现了InstantiationAwareBeanPostProcessor,还记得在“Bean创建过程”小结中介绍的InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation么?如果该步骤生成了“代理”bean instance,则会有一个短路操作,直接返回该bean instance而不再执行后续的doCreate

InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation同样是一个短路操作,如果有任意一个InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法返回false,则会跳出属性注入的逻辑,官方对此的解释如下

Give any InstantiationAwareBeanPostProcessors the opportunity to modify the state of the bean before properties are set. This can be used, for example, to support styles of field injection.

autowireByNameautowireByType方法作为“候补”补充BeanDefinition的propertyValues

Fill in any missing property values with references to other beans.

PropertyValue中记录了需要注入的属性信息及需要注入的属性值,那BeanDefinition的propertyValues都来自哪里?xml中的bean配置、自定义的BeanDefinition等

public class MyService {
    /**
     * string
     */
    private String name;

    /**
     * resource
     */
    private Resource res;

    /**
     * bean ref
     */
    private MyComponent myComponent;
}

xml中定义PropertyValue

<bean id="myMyService" class="com.manerfan.MyService">
  <property name="name" value="SpringDemoApp"></property>
  <property name="res" value="classpath:/init/init.sql"></property>
  <property name="myComponent" ref="myComponent"></property>
</bean>

BeanDefinition中直接定义PropertyValue

// 构建BeanDefinition
BeanDefinition bd = BeanDefinitionBuilder
    .genericBeanDefinition(MyService.class)
    .addPropertyValue("name", "${spring.application.name}")
    .addPropertyValue("res", "classpath:/init/init.sql")
    .addPropertyReference("myComponent", "myComponent")
    .getBeanDefinition();
// 注册BeanDefinition
beanFactory.registerBeanDefinition("myService", bd);

所有的ProperValue均会在AbstractAutowireCapableBeanFactory#applyPropertyValues中进行依赖的解析、转换并设置到bean实例对应的属性中,详细的逻辑下文介绍

注解注入

除此之外通过注解修饰的属性(方法)是如何注入的?

public class MyService {
    /**
     * string
     */
    @Value("${spring.application.name}")
    private String name;

    /**
     * resource
     */
    @Value("classpath:/init/init.sql")
    private Resource res;

    /**
     * bean ref by parameter
     */
    @Autowired
    @Qualifier("myComponent1")
    // or @Resource("myComponent1")
    // or @Inject
    private MyComponent myComponent1;
    
    private MyComponent myComponent2;
    
    /**
     * bean ref by setter method
     */
    @Autowired
    public void setMyComponet2(@Qualifier("myComponent1") MyComponet component) {
        this.myComponent2 = component
    }
}
各注解的使用可以参考 https://www.baeldung.com/spri...

AbstractAutowireCapableBeanFactory#applyPropertyValues之前发现还有一个动作InstantiationAwareBeanPostProcessor#postProcessProperties(是的InstantiationAwareBeanPostProcessor又出现了),在此有两个实现引起了我的注意AutowiredAnnotationBeanPostProcessor#postProcessPropertiesCommonAnnotationBeanPostProcessor#postProcessProperties,我们来对比两个实现的内部逻辑

// AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    } catch (Throwable ex) { /* handle exception */ }
    return pvs;
}
// CommonAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    } catch (Throwable ex) { /* handle exception */ }
    return pvs;
}

Flowchart.postProcessProperties

从代码及流程图可以看出,两种实现的差异仅在InjectionMetadata的查找逻辑,一个个来

AutowiredAnnotation

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata的核心逻辑可以追踪到AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata

Flowchart.buildAutowiringMetadata

CommonAnnotation

CommonAnnotationBeanPostProcessor#findResourceMetadata的核心逻辑可以追踪到CommonAnnotationBeanPostProcessor.buildResourceMetadata

Flowchart.buildResourceMetadata

InjectionElement

以上,对于不同的注解不同的方式(属性/方法),会被封装为不同的InjectionElement,并最终将所有的InjectionElement封装在InjectionMetadata

在找到InjectionElement之后,下一步便是依赖的解析和注入了(InjectionMetadata#inject

这里的逻辑无非就是遍历内部所有的InjectionElement并执行InjectionElement.inject,上面已经介绍,对于不同的注解不同的方式(属性/方法),会被封装为不同的InjectionElement,那不同的InjectionElement也会有不同的inject逻辑,至此我们大致可以理出一个注解注入的大框架

Flowchart.injectByAnnotation

所以,归根结底,注入的过程在AutowiredFieldElementAutowiredMethodElementResourceElement、等InjectionElement内部,在继续进行之前有必要了解一下DefaultListableBeanFactory#resolveDependency

还记得上文“创建bean实体”一节中介绍参数的注入时提到的AutowireCapableBeanFactory#resolveDependency么?该函数正是调用了DefaultListableBeanFactory#resolveDependency,上文并未详细展开该函数的逻辑实现,其除了处理OptionalBean、及LazyBean之外,我们比较关心的逻辑在DefaultListableBeanFactory#doResolveDependency

Flowchart.doResolveDependency

该函数处理了@Value@Qualifier@Primary@Order等的逻辑

@Value的解析有两个过程,1. StringValueResolver解析(${spring.sql.init} -> classpath:/init/init.sql);2. PropertyEditor转换(classpath:/init/init.sql -> Resouce);

AutowiredFieldElement无非就是使用DefaultListableBeanFactory#doResolveDependency将依赖的bean解析到,并设置到对应的属性上

AutowiredMethodElement则是使用DefaultListableBeanFactory#doResolveDependency将参数对应依赖的bean解析到,并执行对应的方法

Q: 我们是否可以自定义注解(InstantiationAwareBeanPostProcessor),来实现类似 @Value@Autowired 的功能?

属性注入

AbstractAutowireCapableBeanFactory#applyPropertyValues

还记得在一开始提到的BeanDefinition中的propertyValues么?(xml中的bean配置、自定义的BeanDefinition,也有可能来自InstantiationAwareBeanPostProcessor#postProcessProperties),至此这一部分的属性还未注入依赖

PropertyValue中记录了需要注入的属性,已经依赖的类型(StringRuntimeBeanReference、等),根据不同的类型解析依赖的bean并设置到对应的属性上(此过程与DefaultListableBeanFactory#doResolveDependency极其相似,不再赘述)

初始化

AbstractAutowireCapableBeanFactory#initializeBean

以上,完成了bean实例的创建和属性注入,之后还有一些初始化的方法,比如各种AwaresetXxx是如何调用的、@PostConstruct是怎么调用的?

Flowchart.initializeBean

Q: Aware类有很多,除了上图中的三种之外,其他的Aware是什么时候调用的?

Q: @PreDestroy是如何调用的?destroy-method是何时执行的?

Q: AbstractAdvisingBeanPostProcessor都做了什么?是如何处理AOP代理的?

注册Disposable

AbstractBeanFactory#registerDisposableBeanIfNecessary

至此,终于完成了bean实例的创建、属性注入以及之后的初始化,此后便可以开始使用了

在使用Spring的过程中经常还会碰到设置销毁逻辑的情况,如数据库连接池、线程池等等,在Spring销毁bean的时候还需要做一些处理,类似于C++中的析构

在bean的创建逻辑中,最后一个步骤则是注册bean的销毁逻辑(DisposableBean)

销毁逻辑的注册有几个条件

  1. 非prototype(singleton或者注册的scope)
  2. 非NullBean
  3. 指定了destroy-method(如xml中指定或者BeanDefinition中直接设置)或者存在@PreDestroy注解的方法(CommonAnnotationBeanPostProcessor.requiresDestruction
if (!mbd.isPrototype() && requiresDestruction(bean, mbd))

满足以上条件的bean会被封装为DisposableBeanAdapter,并注册在DefaultSingletonBeanRegistry.disposableBeans中(详见附录#中间态说明)

Q: 理解了bean的销毁注册逻辑,那bean的销毁时何时触发以及如何执行的?

尝试类型转换

以上,完成了bean的创建、属性的注入、dispose逻辑的注册,但获得的bean类型与实际需要的类型可能依然不相符,在最终交付bean之前(getBean)还需要进行一次类型转换,上文反复提到过PropertyEditor,此处不例外,使用的既是PropertyEditor进行的类型转换,具体的逻辑不再赘述,再将bean转换为真正需要的类型后,便完成了整个getBean的使命

Bean销毁过程

了解了bean的完成创建过程后,那bean是如何销毁的呢?

bean的创建过程始于DefaultListableBeanFactory.getBean,销毁过程则终于ConfigurableApplicationContext#close,跟踪下去,具体的逻辑在DefaultSingletonBeanRegistry#destroySingletons

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
public void destroySingletons() {
    synchronized (this.singletonObjects) {
        this.singletonsCurrentlyInDestruction = true;
    }

    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
    // 遍历注册的DisposableBean
        destroySingleton(disposableBeanNames[i]);
    }

  // 清理各种缓存
    this.containedBeanMap.clear();
    this.dependentBeanMap.clear();
    this.dependenciesForBeanMap.clear();
    clearSingletonCache();
}

在介绍bean创建的时候提到过两个概念

  1. DefaultSingletonBeanRegistry.disposableBeans

    需要注册销毁逻辑的bean会被封装为DisposableBeanAdapter并缓存在此处

  2. DefaultSingletonBeanRegistry.dependentBeanMap

    对于存在依赖注入关系的bean,会将bean的依赖关系缓存在此处(dependentBeanMap: 哪些bean依赖了我; dependenciesForBeanMap: 我依赖了哪些bean)

Flowchart.destroySingleton

从上图中可以看出,bean的销毁顺序与创建顺序正好相反,如果有 beanA --dependsOn--> beanB --> beanC ,创建(getBean)时一定是beanC -> beanB -> beanA,销毁时一定是 beanA -> beanB -> beanC,以此避免因为依赖关系造成的一些异常情况

循环依赖

在介绍Bean创建的时候提到过earlySingletonObject,为了解决循环依赖的问题,在实例化完后属性注入之前会提前将当前的bean实体暴露出来,以防止在属性注入过程中所注入的bean又依赖当前的bean造成的类似“死锁”的状态,但即便有这样的逻辑还是要注意几点

Beans.CircularReferences

显示设置dependsOn的循环依赖

@DependsOn("beanB")
@Component
public class BeanA {}

@DependsOn("beanC")
@Component
public class BeanB {}

@DependsOn("beanA")
@Component
public class BeanC {}

dependsOn的依赖,在bean的创建之前便会处理

Spring在实例化以上bean时,在创建BeanA之前会触发创建BeanB,创建BeanB之前会触发创建BeanC,而创建BeanC之前又会触发创建BeanA,由此引发一个无解的循环依赖

构造函数循环依赖

@Component
public class BeanA {
    public BeanA(BeanB beanB) {
    }
}

@Component
public class BeanB {
    public BeanB(BeanC beanC) {
    }
}

@Component
public class BeanC {
    public BeanC(BeanA beanA) {
    }
}

与dependsOn一样的原理,构造函数参数依赖,同样在bean的创建之前便会处理,从而引发无解的循环依赖

factory-method依赖

@Bean
public BeanA beanA(BeanB beanB) {
    return new BeanA();
}

@Bean
public BeanB beanB(BeanC beanC) {
    return new BeanB();
}

@Bean
public BeanC beanC(BeanA beanA) {
    return new BeanC();
}

原理与上述相同,不再赘述

显示dependsOn、构造函数依赖、factory-method依赖任意混合

@DependsOn("beanB")
@Component
public class BeanA {
}

@Component
public class BeanB {
    public BeanB(BeanC beanC) {
    }
}

@Bean
public BeanC beanC(BeanA beanA) {
    return new BeanC();
}

似乎我们找到了一定的规律,只要一个循环依赖中的所有bean,其依赖关系都需要在创建bean实例之前进行解决,此循环依赖则一定无解

打破无解的循环依赖

还以上述三个Bean为例,先将其中任意一个依赖设置为属性依赖(属性依赖的处理,在bean实例创建完成且暴露earlySingleton之后)

@Component
public class BeanA {
    @Autowired
    private BeanB beanB;
}

@Component
public class BeanB {
    public BeanB(BeanC beanC) {
    }
}

@Bean
public BeanC beanC(BeanA beanA) {
    return new BeanC();
}

@DependsOn("beanB")
@Component
public class BeanA {
}

@Component
public class BeanB {
    private BeanC beanC;

    @Resource
    public void setBeanC(BeanC beanC) {
        this.beanC = beanC;
    }
}

@Bean
public BeanC beanC(BeanA beanA) {
    return new BeanC();
}

等等

为了避免无解的循环依赖,在构成循环依赖的一个环中,只需要保证其中至少一个bean的依赖在该bean创建且暴露earlySingleton之后处理即可

我们以“bean创建且暴露earlySingleton”为节点,在此之前处理依赖的有instance supplier parameterfactory method parameterconstructor parameter、等,在此之后处理的依赖有 class propertysetter parameter、等

小结

本文介绍了Spring体系内bean的创建及销毁过程,在经过万次的commit后,也造就了Spring一定程度上的复杂度

本文并未全方位的诠释bean的创建过程,文中遗留了很多疑问点,同时也能发现Spring提供了众多的扩展点来增强Ioc的能力,让开发者能够更好的使用/驾驭

下一篇文章,将着重介绍Spring本文中遗留的疑问点及Spring所提供的各种扩展能力

附录

中间态说明

所在类 属性 类型 描述
SimpleAliasRegistry aliasMap Map<String, String> Map from alias to canonical name.<br/> 别名alias到原始bean-name的映射
DefaultSingletonBeanRegistry earlySingletonObjects Map<String, Object> Cache of early singleton objects: bean name to bean instance<br/> 缓存提前曝光的singleton-bean-instance,主要用于解决循环依赖,存在于该处的实例还未执行属性依赖注入及其他后处理逻辑
singletonFactories Map<String, ObjectFactory> Cache of singleton factories: bean name to ObjectFactory.<br/> 缓存用于生成、获取earlySingletonObject的工厂方法
singletonObjects Map<String, Object> Cache of singleton objects: bean name to bean instance.<br/> 缓存已经实例化的singleton-bean-instance
dependentBeanMap Map<String, Set<String>> Map between dependent bean names: bean name to Set of dependent bean names.<br/> 如果 beanA -> beanB, beanA -> beanC,key为beanA,value为[beanB, beanC]
dependenciesForBeanMap Map<String, Set<String>> Map between depending bean names: bean name to Set of bean names for the bean's dependencies.<br/> 如果 beanB -> beanA, beanC -> beanA,key为beanA,value为[beanB, beanC]
singletonsCurrentlyInCreation Set<String> Names of beans that are currently in creation.<br/> 缓存正在创建中的singleton-bean-name
registeredSingletons Set<String> Set of registered singletons, containing the bean names in registration order.<br/> 缓存已经创建的singleton-bean-name
disposableBeans Map<String, Object> Disposable bean instances: bean name to disposable instance.<br/> 缓存注册的可销毁bean(bean的销毁逻辑)
FactoryBeanRegistrySupport factoryBeanObjectCache Map<String, Object> Cache of singleton objects created by FactoryBeans: FactoryBean name to object.<br/> 针对singleton,缓存FactoryBean得到的真正的singleton-bean-instance

订阅号

阅读 2.1k

推荐阅读
林中小舍
用户专栏

工作中的坑点及经验

51 人关注
41 篇文章
专栏主页