【p2】关于Spring如何管理Bean的生命周期

(转载请注明作者和出处‘https://fourthringroad.com/’,请勿用于任何商业用途)

上篇文章大概描述了背景,这篇讲讲spring如何管理bean生命周期以及预留的各种hooks。

在进入正题之前先确认第一个问题:

spring管理bean的容器是在哪儿?

作为最核心的接口,我们可以从ApplicatonContext入手,从类的继承关系上可以看出它具备的能力包括:资源加载(ResourceLoader),事件发布(EventPublisher),消息解析(MessageSource),类工厂(BeanFactory)等等

显然其中的工厂类ListableBeanFactory跟Bean的构造最直接相关:

简单浏览ApplicationContext实现类的代码,就可以发现AC本质上是Delegate一个DefaultListableBeanFactory实例去填充上面BeanFactory和ListableBeanFactory中的方法具体逻辑。例如在下面的实现类中就有一个DefaultListableBeanFactory属性:

我们转向DefaultListableBeanFactory,它涉及功能更多,实现的接口与抽象类也更复杂,拆这么细应该也是遵循了设计模式的接口隔离原则,方便后期的维护拓展。它继承的其中一个类是DefaultSingletonBeanRegistry,我们发现这里有存储单例类实例的Map(prototype显然不需要存储):singletonObjets & earlySingletonObjects。

所以问题的答案就是众人皆知的BeanFactory,它的实现类具备了创建,查询以及存储上下文中beans的能力。bean生命周期显然跟它的子类息息相关。

Spring Bean的生命周期

我们先从ApplicationContext最常见的实现类ClassPathXmlApplicationContext的构造函数入手,可以很快定位启动的入口函数AbstractApplicationContext#refresh,这个方法的成功执行意味着spring上下文的成功加载。我们对其中的两个个重要方法进行分析:

BeanFactory实例的构造 – obtainFreshBeanFactory()

文章的开头提到了BeanFactory是bean生命周期直接相关的工厂类,显然AC在启动过程中,初始化BeanFactory过程中做了什么操作是很重要的。我们从这个方法进入可以追踪到如下的初始化代码:

可以看到在创建一个DefaultListableBeanFactory实例之后,有一个加载所有BeanDefinition的操作-loadBeanDefinitions(beanFactory);这个BeanDefinition很好理解,其实就是关于一个Bean的所有定义,BeanFactory会通过这个定义去构造Bean的实例。因为我们使用的是ClassPathXmlApplicationContext,那么假定前提就是我们对bean的定义位于XML配置文件中,这个loadBeanDefinitions方法的实现就位于AbstractXmlApplicationContext类中,如下我们可以看到有构造XML相关的Reader来执行加载过程:

经过了这一步,就拿到了所有bean的定义,那么就可以开始bean的初始化过程了。

普通Bean的初始化 – finishBeanFactoryInitialization(beanFactory)

普通单例类的初始化逻辑的入口位于倒数第二个方法,finishBeanFactoryInitialization(beanFactory);这个方法调用链的底层,是调用BeanFactory#getBean创建bean实例;进一步深入,BeanFactory的子类AbstractAutowireCapableBeanFactory中的方法createBean以及doCreateBean会被调用,如下:

执行完这个流程之后,一个bean就被初始化完成并放入缓存,等待运行时使用以及最后的销毁,实际上这就是是bean的生命周期,红框中包括了这个流程中的核心逻辑,如果接着上文中加载BeanDefinition之后,把接下来代码用一个流程图来展示:

红框中是四个关键步骤:

  • 实例化创建:创建一个bean实例
  • 赋值:给bean实例的属性赋值。
  • 初始化:执行初始化函数(如果有的话)
  • 销毁:执行销毁函数(如果有的话)

其他无色流程框的都是可以自定义的扩展点。spring给四个关键环节提供最简单的接入方法,分别是:默认构造函数,setter方法,init-method, destroy-method。假如使用者完全不介入spring bean的生命周期,显然和new一个对象并进行使用几乎是一样的。

可以看到无色方框是围绕四个关键步骤的扩展点,意味着我们可以在bean的不同阶段进行干预。

除此之外还有一些小的扩展点其实也都是围绕上面的主流程,譬如假如一个bean实现了InitializingBean接:

那么里面的afterPropertiesSet()方法的调用点在哪里呢?实际上就在Initialization步骤里执行init-method之前:

对BeanFactory进行处理的Hooks:

值得一提还有一个针对BeanFactory的钩子:我们在refresh方法中,可以看到在构造BeanFactory的实例之后,有一步:invokeBeanFactoryPostProcessors(),在里面预留了对BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor相应方法的调用,用于对BeanFactory实例进行额外处理。

例子 – ApplicationContextAwareProcesser

在refresh的主流程步骤prepareBeanFactory中,注册了一个spring 官方的BeanPostProcessor- ApplicationContextAwareProcesser,我们来看看它对bean的生命周期进行了什么操作

可以看到它在bean的初始化阶段发生前调用了invokeAwareInterfaces方法,在这个方法里面,依次判断了当前bean是否实现/继承了xxAware接口/类,如果是,则调用相应的set方法把xx实例设置到bean的属性中。这实际上解释了为什么我们在日常使用中,一个类实现了ApplicationContextAware接口,在初始化过程中,以及后续runtime时期就可以在bean内部使用ApplicationContext实例。正是因为ApplicationContextAwareProcesser这个处理器在bean初始化过程中做了手脚。

小结

Spring bean的生命周期其实就是由很多扩展点构成,这里把整体梳理了一遍,但是针对每个拓展点没有深入的讲,有一些小的拓展点也没有提及,用的时候再深入研究即可,没有必要死记硬背。

Spring的拓展性一直为人称道,其实就是很好的遵循了一些编程思想,比如面向接口编程,接口隔离,以及采用了类似Service Provider Interface(SPI)的模式。这些方法也常常在一些扩展性好的,所谓的面向插件编程的系统上见到,比如前一阵在做ES插件开发的时候就深有体会。

但是回过头,像前文提到的一样,这些功能用户可以根据说明文档就很好的把它当成黑盒使用吗?显然是很难的,阅读源码基本上是每一个用户必经之路,这也就是Spring框架,至少在bean管理方面,我认为比较繁重的原因。

此条目发表在spring相关分类目录。将固定链接加入收藏夹。

【p2】关于Spring如何管理Bean的生命周期》有2条回应

  1. 123_bob说:

    good article

  2. brotapova说:

    Patients with urethritis may present to the emergency department, urgent care clinic, or primary care provider priligy dosage Whether the dog s heart is enlarged no longer is the issue

发表评论

您的电子邮箱地址不会被公开。