(转载请注明作者和出处‘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管理方面,我认为比较繁重的原因。
good article
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