夯實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核心編程思想》