夯实Spring系列|第十二章:Spring Bean 生命周期-上篇
本章说明
本文应该是讲 Spring Bean 生命周期最全的一篇,一共细分为 18 个阶段,从 Bean 的配置阶段到最终的销毁阶段,还特别加入了垃圾回收,Spring 设计的各个阶段的切入点都会演示到,而且都会进行源码调试;由于章节太多,所以本章将分为上中下三篇进行发布。
上篇:主要是讨论 Spring Bean 的元信息配置,解析,注册,合并,类加载 5 个阶段,其中后 4 个阶段主要是 Spring 容器内部操作,我们无法进行编码,所以源码分析和调试较多,最好在 Idea 中跟着一起进行调试会比较好理解。
中篇:主要是讨论 Spring 如何将 Class 进行实例化,以及实例化之后的属性赋值阶段,每个阶段均有对应的接口回调方法进行演示。
下篇:主要讨论初始化、销毁以及垃圾回收等 3 个阶段,以及每个阶段对应的接口回调方法。
友情提醒:开始前请备好晕车药~
1.项目环境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模块:bean-lifecycle
2.Spring Bean 元信息配置阶段
BeanDefinition 配置
- 面向资源
- XML 配置
- <bean id="…" …>
- Properties 资源配置
- XML 配置
- 面向注解
- @Configuration、@Component、@Bean
- 面向 API
- BeanDefinitionBuilder
2.1 XML 配置
源码位置:ioc-container-overview 模块
com.huajie.thinking.in.spring.ioc.overview.container.BeanFactoryAsIocContainerDemo
通过 XmlBeanDefinitionReader 来加载 XML 配置
/**
* IOC 容器示例
*/
public class BeanFactoryAsIocContainerDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 加载配置
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/dependency-injection-context.xml";
int beanDefinitionsCount = reader.loadBeanDefinitions(location);
System.out.println("Bean 定义加载的数量:"+beanDefinitionsCount);
lookupCollectionByType(beanFactory);
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有集合对象---" + users);
}
}
}
执行结果
Bean 定义加载的数量:4
查找到的所有集合对象---{user=User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}, superUser=SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}}
2.2 注解 配置
这个相关的示例太多了,前面的章节有很多,这里就不演示了。
第六章:Spring Bean 注册、实例化、初始化、销毁 2.1.1 Java 注解配置元信息
2.3 Properties 资源配置
源码位置:org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
Java Doc 中有相应的示例
同样我们在 resource/META-INF 目录下面 新建一个 user.properties 文件
user 相当于 xml 中的 id,user.(class) 相当于 xml 中的 class,以此类推。
user.(class) = com.huajie.thinking.in.spring.ioc.overview.domain.User
user.id = 001
user.name = 小仙
user.city = WUHAN
使用 PropertiesBeanDefinitionReader
来加载这个 properties 文件
/**
* Bean 生命周期 示例
*/
public class BeanMetadataConfigurationDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory);
String location = "/META-INF/user.properties";
Resource resource = new ClassPathResource(location);
EncodedResource encodedResource = new EncodedResource(resource,"GBK");
int beanDefinitionsCount = reader.loadBeanDefinitions(encodedResource);
System.out.println("Bean 定义加载的数量:"+beanDefinitionsCount);
lookupCollectionByType(beanFactory);
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有集合对象---" + users);
}
}
}
执行结果:
Bean 定义加载的数量:1
查找到的所有集合对象---{user=User{beanName='user', id=1, name='小仙', age=null, configFileReource=null, city=WUHAN, cities=null, lifeCities=null}}
2.4 面向 API
第五章:Spring Bean 定义 4.BeanDefinition 构建 小节
3.Spring Bean 元信息解析阶段
- 面向资源 BeanDefinition 解析
- BeanDefinitionReader
- Xml 解析器 - BeanDefintionParser
- 面向注解 BeanDefinition 解析
- AnnotatedBeanDefinitonReader
3.1 面向资源 BeanDefinition 解析
这两种方式就是 2.Spring Bean 元信息配置阶段 中的 xml 和 properties 文件。
3.2 面向注解 BeanDefinition 解析
这里并不是演示 @Component 以及其派生注解的使用,而是演示 AnnotatedBeanDefinitionReader 这类如何解析注册一个类,换言之 Spring 如何使用 AnnotatedBeanDefinitionReader
将标注有 @Component 的类注册为 BeanDefintion 。
/**
* 注解解析 BeanDefinition 示例
*/
public class AnnotatedBeanDefinitionParserDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
int beanDefinitionCountBefore = beanFactory.getBeanDefinitionCount();
//注册当前类(非 @Component class)
reader.register(AnnotatedBeanDefinitionParserDemo.class);
reader.register(Test.class);
int beanDefinitionCountAfter = beanFactory.getBeanDefinitionCount();
int beanDefinitionCount = beanDefinitionCountAfter - beanDefinitionCountBefore;
System.out.println("Bean 定义注册的数量:"+beanDefinitionCount);
//Bean 名称生成来自于 BeanNameGenerator,注册实现 AnnotatedBeanNameGenerator
AnnotatedBeanDefinitionParserDemo demo = beanFactory.getBean("annotatedBeanDefinitionParserDemo",
AnnotatedBeanDefinitionParserDemo.class);
System.out.println(demo);
}
public class Test{
}
}
执行结果
Bean 定义注册的数量:2
com.huajie.thinking.in.spring.bean.lifecycle.AnnotatedBeanDefinitionParserDemo@31f924f5
4.Spring Bean 注册阶段
BeanDefintion 注册接口
- BeanDefinitionRegistry
唯一实现
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
源码较多,这里省略一部分,只对重点代码进行分析
- beanDefinitionMap 数据结构为 ConcurrentHashMap,没有顺序
- beanDefinitionNames 数据结构为 ArrayList, 存放 beanName 保证注册的顺序
- this.beanDefinitionMap.put(beanName, beanDefinition); //将 beanDefinition 存入 beanDefinitionMap
- removeManualSingletonName,注册的单例对象(并非 Bean Scope)和注册 BeanDefinition 是一个互斥的操作,只能存在一个
省略后部分源码如下:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
// beanDefinition 效验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
...
//是否已经存在 BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//是否允许重复定义 默认是 true
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
...
// 将 beanDefinition 存入 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//如果 Bean 已经开始创建
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {//加锁保证操作的安全性
// 将 beanDefinition 存入 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
...
}
}
else {//正常创建
// Still in startup registration phase
// 将 beanDefinition 存入 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
//主要是为了注册的顺序
this.beanDefinitionNames.add(beanName);
//删除掉注册的单例对象,互斥操作
removeManualSingletonName(beanName);
}
...
}
5.Spring BeanDefinition 合并阶段
BeanDefintion 合并
- 父子 BeanDefinition 合并
- 当前 BeanFactory 查找
- 层次性 BeanFactory 查找
5.1 XML 配置
dependency-lookup-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:component-scan base-package="com"/>-->
<!--<context:annotation-config/>-->
<bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
<property name="name" value="xwf"/>
<property name="age" value="18"/>
<property name="id" value="1"/>
<property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
<property name="city" value="WUHAN"/>
<property name="cities" value="WUHAN,BEIJING"/>
<property name="lifeCities" value="WUHAN,BEIJING"/>
</bean>
<bean primary="true" id="superUser" class="com.huajie.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user">
<property name="address" value="wuhan"/>
</bean>
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
</beans>
user 是一个典型的通过 xml 方式配置的 BeanDefinition,user 没有 parent 属性,可以类比 user 是一个 Root BeanDefintion 不需要合并,实际情况还是一个普通的 BeanDefintion。
- org.springframework.beans.factory.support.RootBeanDefinition
而 superUser 的 parent 属性指向 user,表示 superUser 是一个普通的 BeanDefintion,并且需要合并 user 中的字段属性,这样设计的好处主要是为了优化我们配置的方式。
- org.springframework.beans.factory.support.GenericBeanDefinition
5.2 示例
/**
* BeanDefinition 合并示例
*/
public class MergedBeanDefinitionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源
int count = reader.loadBeanDefinitions(location);
System.out.println(count);
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
Stream.of(beanDefinitionNames).forEach(System.out::println);
User user = beanFactory.getBean("user", User.class);
SuperUser superUser = beanFactory.getBean("superUser", SuperUser.class);
System.out.println(user);
System.out.println(superUser);
}
}
执行结果:
3
user
superUser
objectFactory
User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
我们定义了 3 个 BeanDefinition,分别是 user、superUser、objectFactory,可以看到 SuperUser 合并了 User 的属性;通过这个示例我们来调试下源码。
5.3 源码分析
先分析下相关的源码:
org.springframework.beans.factory.config.ConfigurableBeanFactory#getMergedBeanDefinition
/**
* Return a merged BeanDefinition for the given bean name,
* merging a child bean definition with its parent if necessary.
* Considers bean definitions in ancestor factories as well.
* @param beanName the name of the bean to retrieve the merged definition for
* @return a (potentially merged) BeanDefinition for the given bean
* @throws NoSuchBeanDefinitionException if there is no bean definition with the given name
* @since 2.5
*/
BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
通过 beanName 返回一个被合并的 BeanDefinition,合并 child bean definition 和它的 parent。
这个接口也是只有一个唯一实现
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String)
@Override
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
String beanName = transformedBeanName(name);
// Efficiently check whether bean definition exists in this factory.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
}
// Resolve merged bean definition locally.
return getMergedLocalBeanDefinition(beanName);
}
这是一个递归的方法,如果当前 BeanFactory 不包含这个 beanName 并且 Parent BeanFactory 是 ConfigurableBeanFactory 这个类型,那么继续往下查找,如果有的话在当前的 BeanFactory 中查找。
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
// 如果 mbd 不为空 并且 没有过期
if (mbd != null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
在 mergedBeanDefinitions 这个集合中查找,这个集合是合并之后的 BeanDefinition 存放的集合,集合元素类型为 RootBeanDefinition,这里需要注意的是 mergedBeanDefinitions 表示的只是当前 BeanFactory 中的 BeanDefinition,如果有多层 BeanFactory ,每个 BeanFactory 都会有这个 mergedBeanDefinitions 的缓存。
第一次进来肯定是没有的,我们继续往下看,最终到这个方法中,参数中的 containingBd 表示的是嵌套 Bean 的情况,我们这里不讨论。
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
// 下面的操作既有 get 也有 put,需要加锁保证安全性,而且这个方法可能在很多地方调用
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
RootBeanDefinition previous = null;
// Check with full lock now in order to enforce the same merged instance.
// 如果为空,表示当前的 BeanDefintion 并不是一个嵌套 Bean 而是顶层 Bean
if (containingBd == null) {
// 再从 mergedBeanDefinitions 获取,这里主要是防止有其他线程已经添加了这个 BeanDefinition
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null || mbd.stale) {
previous = mbd;
if (bd.getParentName() == null) {
// Use copy of given root bean definition.
if (bd instanceof RootBeanDefinition) {
// 如果是 RootBeanDefinition 直接返回
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
// 构建一个 RootBeanDefinition
mbd = new RootBeanDefinition(bd);
}
}
else {
// 示例中 SuperUser 属于这一种情况
// Child bean definition: needs to be merged with parent.
BeanDefinition pbd;
try {
// 获取 parent 属性中 BeanName
String parentBeanName = transformedBeanName(bd.getParentName());
// 如果 beanName和parentBeanName不相同
if (!beanName.equals(parentBeanName)) {
// 获取 parent 的合并之后的 BeanDefintion,因为 parent 有可能也是一个被合并的 BeanDefintion
pbd = getMergedBeanDefinition(parentBeanName);
}
else {// 如果相同的话,去 parent BeanFactory 中做层次性的查找
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
...
// Deep copy with overridden values.
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
...
if (containingBd == null && isCacheBeanMetadata()) {
// 将合并之后的 mbd 存放到 mergedBeanDefinitions 集合中
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}
5.4 源码调试
局部变量命名说明
-
bd -> BeanDefinition
-
mdb -> MergedBeanDefinition 被合并(Merged)之后的 BeanDefinition
断点打在 AbstractBeanFactory#getMergedBeanDefinition() 1309 行
第一次 user 进来 mergedBeanDefinitions 集合为空,此时参数 beanName = user
mbd 必然也是为 null,而且 user 没有 parent 属性,所以 bd.getParentName() == null 成立,bd 目前还是普通的 BeanDefinition,
合并完成,将 mdb 存入 mergedBeanDefinitions 集合,最后返回 mdb 对象。
第二次 superUser 进来,此时 mergedBeanDefinitions 集合中已经存在了user的 RootBeanDefintion 对象。
区别在于这次 bd.getParentName() == null 不成立,所以进入下面的逻辑
最终获取到 superUser 的 parent BeanDefintion(也就是 user 的 BeanDefintion)
通过 mbd.overrideFrom(bd) 进行合并操作
再将 superUser 存入到 mergedBeanDefinitions 并返回 superUser 合并之后的 mdb 对象。
5.5 结论
通过上面的调试过程和源码分析可知
- user 和 superUser 都会经过合并的过程,而且最后都变成了 RootBeanDefintion
- superUser 合并了 user 的属性
6.Spring Bean Class 加载阶段
ClassLoader 类加载
Java Security 安全控制
ConfigurableBeanFactory 临时 ClassLoader
6.1 源码调试
还是用上面的例子进行,我们将断点打在
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 249 行
第一次 user 进来
1.中间的一些过程跳过,比如 getSingleton(beanName)
因为我们是通过 XML 的方式配置的 BeanDefinition,并不是通过 registerSingleton 这种方式,所以这一段跳过
2.user 没有 parentBeanFactory,这一段也跳过
3.dependsOn 这种配置方式我们也没有采用,继续跳过
4.我们将断点打在 320 行,因为 BeanDefintion 默认是 singleton 作用域,这个 mbd.isSingleton() 判断成立
createBean 方法中 resolveBeanClass 方法就是类加载过程
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
// 返回user的Class,之前是字符串类型,处理完之后返回 Class 类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
...
}
AbstractBeanFactory#doResolveBeanClass() 1508 行
可以到看到 mbd.getBeanClassName() 的属性的类型是 String 类型,这里还不是真正的 Class。
AbstractBeanDefinition#resolveBeanClass 通过 forName 加载这个 Bean 的 Class,得到这个 resolvedClass 的 Class 并返回。
public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
String className = getBeanClassName();
if (className == null) {
return null;
}
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
this.beanClass = resolvedClass;
return resolvedClass;
}
6.2 结论
从源码分析可以看出,其实 Spring BeanDefinition 变成 Class 的过程其实还是通过传统 Java 的 ClassLoader 来进行加载的。
7.参考
- 极客时间-小马哥《小马哥讲Spring核心编程思想》