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

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

BeanDefinition

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

BeanDefinition衍生出一系列实现类

  • AbstractBeanDefinition
    如同其他Spring类,大部分BeanDefinition接口的逻辑都由该抽象类实现
  • GenericBeanDefinitionGenericBeanDefinition是一站式、用户可见的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来保存bean-name和BeanDefinition的关系
  • 别名的注册定义在AliasRegistry#registerAlias,其实现同样使用一个Map来保存别名alias-name和bean-name(或另一个别名alias-name)的关系

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

BeanFactory

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

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

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

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

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

尝试获取单例

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

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

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

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

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

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

  • singletonObjects
    缓存已经实例化完成的singleton-bean
  • earlySingletonObjects
    缓存正在实例化的、提前曝光的singleton-bean,用于处理循环依赖
  • singletonFactories
    缓存用于生成earlySingletonObjectObjectFactory

ObjectFactory,定义了一个用于创建、生成对象实例的工厂方法java @FunctionalInterface public interface ObjectFactory<T> { T getObject() throws BeansException; }

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

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

FactoryBean处理(sharedInstance存在的逻辑)

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

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

How to use the Spring FactoryBean? | Baeldung​www.baeldung.com

@Bean(initMethod = "init", destroyMethod = "destroy")
public FactoryBean myBean() {return new FactoryBean<MyBean>() {/*** 定制bean初始化逻辑*/@Overridepublic MyBean getObject() throws Exception {MyBean myBean = new MyBean();// ... 定制化的逻辑return myBean;}/*** 真正的bean类型*/@Overridepublic Class<?> getObjectType() {return MyBean.class;}@Overridepublic 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函数的处理逻辑

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

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

简单来讲,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,加载依赖beantry { 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 - ObjectFactorytry { 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的实现逻辑

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

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

  1. resolveBeanClass

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

2. 验证method overrides

在 [spring-framework] [2] BeanDefinitionReader 一文中有提到过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 instanceObject beanInstance = doCreateBean(beanName, mbdToUse, args);// ...
}

Q: InstantiationAwareBeanPostProcessor的使用场景有哪些?Spring有哪些功能使用了InstantiationAwareBeanPostProcessor?它们是在什么时候注册的?

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

创建bean实体

AbstractAutowireCapableBeanFactory#createBeanInstance

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

1. instance supplier

AbstractAutowireCapableBeanFactory#obtainFromSupplier

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

// 注册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 {@Beanpublic 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中定义的或者注解中定义的),在[spring-framework] [2] BeanDefinitionReader 一文中有过介绍,ref会被封装为RuntimeBeanReference存储、value会被封装为TypedStringValue存储等等,如何将这些封装好的存储类型转为真正需要的函数参数,便是ConstructorResolver#resolvePreparedArguments函数的作用

这里分成了三大分支

  1. 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在接下来的发展中可能还会不断地扩充

2. String

AbstractBeanFactory#evaluateBeanDefinitionString

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

3. 其他

  • InjectionPoint
    使用ThreadLocal提供当前bean被注入到的注入点,可以参考

What's New in Spring 4.3? | Baeldung​www.baeldung.com

@Bean
@Scope("prototype")
public Logger logger(InjectionPoint injectionPoint) {// return MyComponent.classreturn Logger.getLogger(injectionPoint.getMethodParameter().getContainingClass());
}@Component
public class MyComponent {@Autowiredprivate 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,内部并没有统一利用反射技术直接使用构造函数创建,而是分为两种情况

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

流程图中两次出现了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 @Injectprivate MyComponent myComponent1;private MyComponent myComponent2;/*** bean ref by setter method*/@Autowiredpublic void setMyComponet2(@Qualifier("myComponent1") MyComponet component) {this.myComponent2 = component}
}

各注解的使用可以参考 https://www.baeldung.com/spring-annotations-resource-inject-autowire

Wiring in Spring: @Autowired, @Resource and @Inject | Baeldung​www.baeldung.com

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;
}

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

AutowiredAnnotation

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

CommonAnnotation

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

InjectionElement

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

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

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

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

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

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--) {// 遍历注册的DisposableBeandestroySingleton(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)

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

循环依赖

在介绍Bean创建的时候提到过earlySingletonObject,为了解决循环依赖的问题,在实例化完后属性注入之前会提前将当前的bean实体暴露出来,以防止在属性注入过程中所注入的bean又依赖当前的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 {@Autowiredprivate 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;@Resourcepublic 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所提供的各种扩展能力

附录

中间态说明


林中通幽径,深山藏小舍

不一样 使用别名 数据字段和bean_【修炼内功】[spring-framework] [3] Bean是如何创建又是如何销毁的?...相关推荐

  1. 不一样 使用别名 数据字段和bean_Mybatis-resultMap标签详解

    是Maybatis的结果集封装,搭配等标签的resultMap属性使用 属性: id:该封装规则的唯一标识 type:封装出来的类型,可以是jdk自带的,比如Map,应该可以是自定义的,比如Emplo ...

  2. mysql union all 别名_MySQL Union合并查询数据及表别名、字段别名用法分析

    本文实例讲述了MySQL Union合并查询数据及表别名.字段别名用法.分享给大家供大家参考,具体如下: union关键字 SELECT s_id, f_name, f_price FROM frui ...

  3. Tableau desktop(二)--数据连接及数据字段

    由于最近比较忙,没有及时更新博客,今天继续前边,记录一下tableau的学习过程. 2.数据连接及数据字段 要开始分析数据,首先需要连接到一个或多个数据源.数据源从简单的Excel工作簿,到复杂的SQ ...

  4. 一个udp用户数据报的数据字段为8192_基于FPGA的千兆网UDP通信分析

    千兆网UDP通信 以太网帧格式 图8‑12 以太网帧格式 表8‑5 以太网帧格式说明 UDP协议分析 为什么UDP协议在FPGA实现时很受欢迎,最主要一个原因就是简单,简答到什么地步呢?UDP协议只是 ...

  5. LightSwitch 2011 数据字段唯一性验证方案

    LightSwitch 2011 数据字段唯一性验证方案 验证单表数据的某个字段不能输入重复值 设置实体字段唯一索引 如果不写代码,那么验证只会在用户提交[保存]数据后,会提示错误,很明显这样的用户体 ...

  6. 数据值、列类型和数据字段属性

    数据值:数值型.字符型.日期型和空值等. 数据列类型 2.1 数值类的数据列类型 2.2 字符串类数据列类型 2.3 日期和时间型数据数据列类型 另外,也可以使用整形列类型存储UNIX时间戳,代替日期 ...

  7. WordPress数据表wp-options数据字段存JSON数据

    2019独角兽企业重金招聘Python工程师标准>>> wp_options表是WordPress中最重要的表,一切程序设置.主题设置和绝大多数插件的设置大都保存在此表. WordP ...

  8. python《pandas入门》实现Excel数据字段的拆分

    ** python<pandas入门>实现Excel数据字段的拆分 ,简单说一下自己为什么写博客,主要原因如下: 1,先简单介绍下自己,我是药学本科毕业,工作几年了.在这几年里做过药物相关 ...

  9. 【计算机网络】——习题解析:一个UDP用户数据的数据字段为8192字节,在数据链路层要使用以太网来传输,试问应当划分为几个IP数据报片?说明每一个IP数据报字段长度和片偏移字段的值

    [计算机网络]--习题解析:一个UDP用户数据的数据字段为8192字节,在数据链路层要使用以太网来传输,试问应当划分为几个IP数据报片?说明每一个IP数据报字段长度和片偏移字段的值 答:6个数据字段的 ...

最新文章

  1. 在读研、竞赛和实习中追求平衡
  2. 大数据发展新契机:中国人工智能产业创新联盟在京成立
  3. mysql登录之后可以写什么_MYSQL登陆完之后如何操作???(新手求助)
  4. virtualenv模块使用
  5. activiti脚本任务_Activiti中的安全脚本如何工作
  6. Win7 U盘安装Ubuntu16.04 双系统
  7. Docker(三) 使用容器数据卷实现数据持久化与容器数据共享
  8. hash表冲突处理方法
  9. h3cne证书有必要考吗_大学生考幼师资格证有必要吗?有什么好处呢
  10. Java Web:session
  11. kafka消费的三种模式_kafka消费者的三种模式(最多/最少/恰好消费一次)
  12. mysql使用总结(C#)
  13. MATLAB中text函数使用
  14. 分布式文件存储MinIO SeaweedFS FastDFS对比总结
  15. 文本表示(一)—— word2vec(skip-gram CBOW) glove, transformer, BERT
  16. IDEA中如何设置滚轮缩放字体大小
  17. 模糊神经网络 matlab,模糊神经网络程序代码
  18. JAR包与JAD反编译
  19. wpf修改鼠标悬停效果
  20. 如果你只知道开心网,那你就OUT了。。。。。

热门文章

  1. 在SharePoint网站中访问Webservice被拒绝的解决方法
  2. ajax核心技术1---XMLHttpRequset对象的使用
  3. linux公司常用基础命令必知必会一
  4. ubuntu设置静态ip不能联网
  5. RHEL6入门系列之二十六,利用rpm进行软件包管理
  6. Linux下获取CPUID、硬盘序列号与MAC地址
  7. Oracle中如何删除重复数据
  8. 魔改和上线你的合成大西瓜,最全教程!
  9. 飞鸽传书从天齐庙南门出来
  10. 局域网聊天老大——飞鸽传书