文章目錄
夯實Spring系列|第十三章:Spring Bean 生命週期-中篇
本章說明
本文是 Spring Bean 生命週期系列的中篇,主要是針對 實例化階段 和實例化之後 屬性賦值階段 的分析和討論。
1.項目環境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模塊:bean-lifecycle
2.Spring Bean 實例化前階段
非主流生命週期 - Bean 實例化前階段
- org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
這個階段在實際工作中很少會用到,而且是個非主流的操作,慎用。
可以看下這個接口方法的 Java Doc
Apply this BeanPostProcessor before the target bean gets instantiated.
The returned bean object may be a proxy to use instead of the target bean,
這個 Bean 的前置處理在目標 bean 實例化之前,返回一個 bean 的對象可能是一個代理對象去替換這個目標的 bean。
2.1 示例
-
自定義類來實現 InstantiationAwareBeanPostProcessor 接口,重寫 postProcessBeforeInstantiation 方法
-
在這個方法中,我們根據 BeanName 來攔截 superUser,替換成一個新的 SuperUser 對象。
-
通過 beanFactory.addBeanPostProcessor 來增加我們的實例化前的操作
/**
* Bean 實例化生命週期
*/
public class BeanInstantiationLifecycleDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//添加 BeanPostBeanProcess 實現
beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcess());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/dependency-lookup-context.xml";
int count = reader.loadBeanDefinitions(location);
System.out.println("Bean 定義的數量: "+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);
}
static class MyInstantiationAwareBeanPostProcess implements InstantiationAwareBeanPostProcessor {
/**
* 實例化前階段
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals(beanName, "superUser") && beanClass.equals(SuperUser.class)) {
SuperUser user = new SuperUser();
user.setName("new-superUser v1");
return user;
}
return null;
}
}
執行結果:
Bean 定義的數量: 3
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='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
2.2 調試
我們將斷點打在示例中的 45 行,也就是我們實現的 postProcessBeforeInstantiation 方法
第一次 user 進來,通過 Idea 左下角的棧信息調用鏈,我們定位到 createBean() 方法
將斷點打在 507行,可以看到 resolveBeforeInstantiation() 方法返回的是 null,程序接着往下執行
最終我可以發現 user 對象是通過 AbstractAutowireCapableBeanFactory#createBean() 中的 517 行的 doGreatebean() 方法來創建 Bean 的實例化對象。
第二次 superUser 進來,createBean 507 行斷點處,可以看到從 resolveBeforeInstantiation() 方法獲取到的對象是被我們替換之後的 SuperUser (new-superUser v1),因爲 bean 不爲 null,就直接返回了,並沒有走後面的邏輯。
那麼大致可以猜到,我們實現的接口方法 postProcessBeforeInstantiation 應該是在 resolveBeforeInstantiation() 方法中被執行。
我們再繼續往下尋找源碼,最終調用我們自定義邏輯的代碼在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation()中,源碼如下:
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
代碼比較簡單,遍歷我們所有的 BeanPostProcessor,如果 BeanPostProcessor 類型爲 InstantiationAwareBeanPostProcessor ,執行並返回執行結果。
2.3 結論
-
原本我們 Spring 默認實例化的 Bean 會被替換成一個代理對象
-
如果這個階段返回了我們替換之後的對象,後面的 Spring Bean 其他生命週期其他過程都不會執行
3.Spring Bean 實例化階段
實例化方法
- 傳統實例化方式
- 實例化策略- InstantiationStrategy
- 構造器依賴注入
關鍵源碼位置
AbstractAutowireCapableBeanFactory#doCreateBean
3.1 源碼分析
通過上一小節的源碼調試,我們可以知道 Spring Bean 默認實例化的方法是 doCreateBean(),在這方法中又可以找到 createBeanInstance(),這個就是 user 創建的調用代碼
- 通過 java8 中的函數式接口進行創建
- 通過 FactoryMethod 方式進行創建
以上兩種方式在本例子中都不存在,可以跳過
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
...
// 通過 java8 中的函數式接口進行創建
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 通過 FactoryMethod 方式進行創建
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 重複創建相同的 bean,代碼邏輯和下面的差不多,會少一些判斷,這是一種性能的優化
...
// Candidate constructors for autowiring?
// 可以提供一些構造器的選擇策略 ctors 通常爲 null
// 自動綁定爲構造器綁定
// 或者 beanDefintion 中包含構造器參數
// 獲取 args 不爲空
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
// 是否有默認的偏好構造器
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// 非特殊方式:簡單實用無參構造器
return instantiateBean(beanName, mbd);
}
從源碼分析中可以知道有兩種實例化的方式
3.1.1 傳統實例化方式 InstantiationStrategy
- instantiateBean:非特殊方式:簡單實用無參構造器
- 源代碼較多,我們這裏直接說關鍵源碼
- SimpleInstantiationStrategy#instantiate()
- 最終是通過 ctor.newInstance(argsWithDefaultValues); 來進行實例化,其實就是 Java 反射中的方法。
- 源碼位置:org.springframework.beans.BeanUtils#instantiateClass(java.lang.reflect.Constructor, java.lang.Object…) 200 行
最後獲取到實例化之後的 User 對象,被封裝成 BeanWrapper 對象,這個對象比較複雜,我們這裏不進行展開
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
...
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
// 封裝成 BeanWrapper 對象
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
// 初始化 BeanWrapper 對象
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
這裏 User 對象通過無參的構造器實例化完成,所有的屬性都是 null,這就是 Spring Bean 實例化的過程,此時還沒有進行屬性賦值和初始化相關的過程。
3.1.2 構造器依賴注入
3.1.2.1 示例改造
autowireConstructor:需要對上面的例子進行改造
我們增加一個 UserHolder 類,使用構造器注入的方式,注入 User 對象。
/**
* UserHolder
*/
public class UserHolder {
private User user;
public UserHolder(User user) {
this.user = user;
}
@Override
public String toString() {
return "UserHolder{" +
"user=" + user +
'}';
}
}
新增 bean-constructor-injection.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/META-INF/dependency-lookup-context.xml"/>
<bean id="userHolder" class="com.huajie.thinking.in.spring.bean.lifecycle.domain.UserHolder"
autowire="constructor"/>
</beans>
示例修改
/**
* Bean 實例化生命週期
*/
public class BeanInstantiationLifecycleDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//添加 BeanpostBeanProcess 實現
beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcess());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String[] locations = {"classpath:/META-INF/dependency-lookup-context.xml","classpath:/META-INF/bean-constructor-injection.xml"};
int count = reader.loadBeanDefinitions(locations);
System.out.println("Bean 定義的數量: " + 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);
UserHolder userHolder = beanFactory.getBean("userHolder", UserHolder.class);
System.out.println(user);
System.out.println(superUser);
System.out.println(userHolder);
}
static class MyInstantiationAwareBeanPostProcess implements InstantiationAwareBeanPostProcessor {
/**
* 實例化前階段
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals(beanName, "superUser") && beanClass.equals(SuperUser.class)) {
SuperUser user = new SuperUser();
user.setName("new-superUser v1");
return user;
}
return null;
}
}
}
執行結果:
Bean 定義的數量: 4
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='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
UserHolder{user=SuperUser{address='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}}
從執行結果可以看到,通過構造器注入 UserHolder 的對象是 SuperUser,通過結果我們大致可以猜到,帶參構造器注入場景中,參數中的 User 對象是通過類型的方式進入注入的,因爲我們的當前上下文中有兩個 User.class 類型的對象,但是 SuperUser 的 primary 屬性爲 true,所以注入的是 SuperUser 對象,因爲如果是通過 user 名稱進行注入的話,應該注入的是 User 對象。
3.1.2.2 源碼調試
斷點打在 AbstractAutowireCapableBeanFactory#createBeanInstance 1162 行,放掉前面的幾個對象,當 beanName 爲 Holder 我們開始進行調試
將斷點打在 AbstractAutowireCapableBeanFactory#createBeanInstance() 1201 行,可以看到滿足第二個條件
mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR,即 BeanDefintion 的自動綁定方式是構造器
繼續往下探源碼
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
ConstructorResolver#autowireConstructor() 這個方法源碼也非常多,我們省略部分場景,關注重點過程即可;
因爲進來這個方法之前的判斷,是有多個或者的判斷條件,所以此方法裏面又會進行相同的判斷,以便確定具體場景,後面有重要節點的調試截圖
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 這個 Args 我們沒配置過 所有爲null 跳過
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
... // FactoryMethod場景
}
...
if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
// 獲取beanDefintion的class,這個 class 就是我們加載階段從 String 替換成的 Class
Class<?> beanClass = mbd.getBeanClass();
try {
// 調試點1
// mbd.isNonPublicAccessAllowed() 默認爲true 則返回所有的構造器(沒有限定修飾符)
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
...//異常處理
// 調試點2
// 通過上面我們獲取到 UserHolder 的構造器,可以滿足這個條件
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
// 但是我們的構造器是帶參數的不滿足這個條件,跳過
if (uniqueCandidate.getParameterCount() == 0) {
...
}
}
// Need to resolve the constructor.
// 調試點3
// 我們前面進來的時候已經確定是構造器注入 autowiring = ture
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
...//無此場景
// 對構造器進行排序
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
...
ArgumentsHolder argsHolder;
// 調試點4
// 獲取參數類型
Class<?>[] paramTypes = candidate.getParameterTypes();
//對構造器的參數進行分析
if (resolvedValues != null) {
try {
// 調試點5
// 獲取 ConstructorProperties註解 配置的參數名稱
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
// 獲取參數名稱處理器
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
// 調試點6
// 創建參數數組,這個方法等會要單獨分析,因爲我們參數本身也是通過注入方式,會觸發依賴處理過程
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
...
Assert.state(argsToUse != null, "Unresolved constructor arguments");
// 調試點7
// 設置 BeanWrapper 的 BeanInstance,並返回
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
調試點 1 獲取 candidates 構造器信息
獲取 UserHolder 構造器,這裏我們只定義了一個
調試點2 因爲我們的構造器有一個參數(result = 1),所以並不會進入這段邏輯
調試點3 autowiring 爲 true,因爲滿足 mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR 這個其實在外面已經判斷過了
調試點4 獲取構造器的參數相關信息
參數類型爲 User.class
調試點5 參數名稱爲 user
-
ConstructorPropertiesChecker 是 Java Beans 裏面的規範,可以通過註解的方式配置參數名稱,這裏我們沒有配置所以爲 null
-
ParameterNameDiscoverer 類,這個類是 Spring 專門用來做參數名稱獲取的
調試點6 依賴處理
ConstructorResolver#createArgumentArray
此方法中也會判斷多種構造器參數的場景,本例子的場景在 779 行之後
resolveAutowiredArgument
這個方法會觸發依賴處理,原因就是 User 這個參數需要注入,這個 beanFactory.resolveDependency
我們在之前的章節 第九章:IoC 依賴注入(專題)-下 7.依賴處理過程 小節中分析過這裏就不展開了,我們直接看結果
返回的參數爲 SuperUser,因爲我們之前的例子,在實例化之前對 SuperUser 做了攔截,所以這裏返回的是我們攔截之後的代理對象。
然後也是通過 BeanUtils#instantiateClass(java.lang.reflect.Constructor, java.lang.Object…)
ctor.newInstance(argsWithDefaultValues);
帶參數的 newInstance 來實例化,和 user 實例化的方法不同,user 是無參的構造。
調試點7
最後 bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); 將實例化的SuperUser 對象放到 BeanWrapper 中 並返回。
因此我們可以得到一個結論:
構造器注入默認是按照類型注入,底層方法是 BeanFactory#resolveDependecy()
4.Spring Bean 實例化後階段
Bean 屬性值(Populate)判斷
- InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
4.1 示例改造
我們在上面示例的基礎上,在 MyInstantiationAwareBeanPostProcess 新增一個 postProcessAfterInstantiation 的重載方法
/**
* Bean 實例化生命週期
*/
public class BeanInstantiationLifecycleDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//添加 BeanpostBeanProcess 實現
beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcess());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String[] locations = {"classpath:/META-INF/dependency-lookup-context.xml", "classpath:/META-INF/bean-constructor-injection.xml"};
int count = reader.loadBeanDefinitions(locations);
System.out.println("Bean 定義的數量: " + 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);
// 構造器注入是按照類型注入,resolveDependency
UserHolder userHolder = beanFactory.getBean("userHolder", UserHolder.class);
System.out.println(user);
System.out.println(superUser);
System.out.println(userHolder);
}
static class MyInstantiationAwareBeanPostProcess implements InstantiationAwareBeanPostProcessor {
/**
* 實例化前階段
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals(beanName, "superUser") && SuperUser.class.equals(beanClass)) {
SuperUser user = new SuperUser();
user.setName("new-superUser v1");
return user;
}
return null;
}
/**
* 實例化後階段
*/
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals(beanName, "user") && User.class.equals(bean.getClass())) {
//"user" 對象不允許屬性賦值(配置元信息-> 屬性值)
User user = User.class.cast(bean);
user.setId(2L);
user.setName("after-superUser v2");
return false;//返回 false 表示忽略掉配置元信息,比如 <bean ...<property name = id value = 1/>
}
return true;
}
}
}
執行結果:
Bean 定義的數量: 4
User{beanName='user', id=2, name='after-superUser v2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
SuperUser{address='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
UserHolder{user=SuperUser{address='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}}
可以看到 User 對象的 id 和 name 屬性被我們替換掉了。
4.2 源碼調試
斷點打在 AbstractAutowireCapableBeanFactory#populateBean 594 行,當 User 實例化之後,會執行該方法
進入到方法中,這裏邏輯比較簡單,遍歷所有的 BeanPostProcessors,找到類型爲 InstantiationAwareBeanPostProcessor 的(這裏就是我們實現的 MyInstantiationAwareBeanPostProcess 類),執行 postProcessAfterInstantiation 方法。
如果返回結果爲 false,程序直接 return,後面的操作(屬性賦值)不會執行,當然這個接口默認返回 true 。
4.3 結論
postProcessAfterInstantiation 方法可以用來判斷這個 Bean 是否要進行屬性賦值(populateBean),如果不需要可以進行攔截返回 false ;這個時候 Bean 的實例化已經完成,但是屬性賦值還沒有開始。
5.Spring Bean 屬性賦值前階段
Bean 屬性值元信息
- PropertyValues
Bean 屬性賦值前回調
-
Spring 1.2 - 5.0 :
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessPropertyValues
-
Spring 5.1:
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties
5.1 示例改造
首先 UserHolder 增加兩個屬性, numebr 和 description
public class UserHolder {
private User user;
private Integer number;
private String description;
public UserHolder(User user) {
this.user = user;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "UserHolder{" +
"user=" + user +
", number=" + number +
", description='" + description + '\'' +
'}';
}
}
對應的 xml 配置如下,我們只設置 description 的值爲 “The user holder”
<bean id="userHolder" class="com.huajie.thinking.in.spring.bean.lifecycle.domain.UserHolder"
autowire="constructor">
<!--<property name="number" value="1"/>-->
<property name="description" value="The user holder" />
</bean>
我們在上面示例的基礎上,在 MyInstantiationAwareBeanPostProcess 新增一個 postProcessProperties 的重載方法
- 設置 numebr 屬性值爲 1
- 覆蓋 description 的屬性爲 “The user holder v2”
/**
* Bean 實例化生命週期
*/
public class BeanInstantiationLifecycleDemo {
... //省略掉 main 方法,和之前的代碼一樣
static class MyInstantiationAwareBeanPostProcess implements InstantiationAwareBeanPostProcessor {
/**
* 實例化前階段
*/
...//省略掉 postProcessBeforeInstantiation 方法
/**
* 實例化後階段
*/
...//省略掉 postProcessAfterInstantiation 方法
// user 跳過 Bean 屬性賦值
// superUser 也是完全跳過 Bean 實例化
// 這裏我們只能攔截 UserHolder
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals(beanName, "userHolder") && UserHolder.class.equals(bean.getClass())) {
final MutablePropertyValues propertyValues;
// 兼容 pvs 爲空的情況 pvs 對應xml中配置的 <property .../>
if (pvs instanceof MutablePropertyValues) {
propertyValues = (MutablePropertyValues) pvs;
} else {
propertyValues = new MutablePropertyValues();
}
// 此操作等價於 <property name="number" value="1"/>
propertyValues.addPropertyValue("number", "1");
if(propertyValues.contains("description")){
// PropertyValue 無法直接覆蓋,因爲是 final 類型
PropertyValue description = propertyValues.getPropertyValue("description");
propertyValues.removePropertyValue("description");
propertyValues.addPropertyValue("description","The user holder v2");
}
return propertyValues;
}
return null;
}
}
}
執行結果:
UserHolder{user=SuperUser{address='null'}User{beanName='null', id=null, name='new-superUser v1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, number=1, description='The user holder v2'}
可以看到 numer =1 , 而且 description 被替換成 ‘The user holder v2’
5.2 源碼調試
斷點位置 AbstractAutowireCapableBeanFactory#populateBean 1395 行,當 UserHolder 進來的時候,會走這段邏輯,可以看到此時 pvs
只有 description 一個屬性,因爲我們 xml 文件中只配置了這一個屬性。
在 1419 行通過遍歷 BeanPostProcessors 執行我們的 postProcessPropertyValues 方法,pvs 的 length 變成了 2,此時屬性已經變成 number = 1,description = ‘The user holder v2’。
最後通過 applyPropertyValues 方法將最新的屬性設置到 bw(BeanWrapper)對象中
6.Spring Bean 屬性賦值階段
還是上面的例子,applyPropertyValues 其實就是屬性賦值階段。
源碼位置 AbstractAutowireCapableBeanFactory#applyPropertyValues
源碼相對比較簡單,我們就不貼出來了,大致分爲三步
- 遍歷我們前面傳進來的
pvs
參數 - 進行
deepCopy
深拷貝 - 然後通過
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
進行賦值操作
斷點打在 AbstractAutowireCapableBeanFactory#applyPropertyValues 1732 行
7.參考
- 極客時間-小馬哥《小馬哥講Spring核心編程思想》