转载

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

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

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

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

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

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

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

BeanDefinition

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

BeanDefinition衍生出一系列实现类

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

  • 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 相关的类依赖图

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

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

  • 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的实例化过程虽然复杂,但大体逻辑非常清楚

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

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

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

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

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 下文会介绍)

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

尝试获取单例

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

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

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

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

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

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

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

  • singletonObjects

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

  • earlySingletonObjects

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

  • singletonFactories

    缓存用于生成 earlySingletonObject ObjectFactory

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

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

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

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

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

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

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.

该函数要实现的逻辑比较简单,如果 sharedInstance FactoryBean ,则使用 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函数的处理逻辑

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

上图中有一个逻辑判断,如果入参 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 )

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

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的加载

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

简单来讲,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的实现逻辑

<img src="../notes/BeanFactory/Flowchart.createSingleton.jpg" alt="Flowchart.createSingleton" style="zoom:50%;" />

所以,关键的逻辑在 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

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

加载其他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中,而在此之前有一些准备工作,整体流程如下图

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

  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创建及初始化逻辑的复杂程度完全超出了本篇文章之承载,这里只对一些关键的逻辑做梳理

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

创建bean实体

AbstractAutowireCapableBeanFactory#createBeanInstance

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

从上面的流程图可以看出,创建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-method factory-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 函数的作用

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

这里分成了三大分支

  • 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 ,内部并没有统一利用反射技术直接使用构造函数创建,而是分为两种情况

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

一种,没有设置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方式及有参构造函数方式的参数注入逻辑,除此之外还有一种注入便是属性注入

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

流程图中两次出现了 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.

autowireByName及 autowireByType 方法作为“候补”补充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;
}

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

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

AutowiredAnnotation

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

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

CommonAnnotation

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

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

InjectionElement

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

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

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

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

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

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

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

该函数处理了 @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是怎么调用的?

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

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)

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

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

循环依赖

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

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

显示设置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

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

原文  https://segmentfault.com/a/1190000022309143
正文到此结束
Loading...