Spring Bean生命周期之BeanDefinition的合并過程詳解
寫在前面
注:本文章使用的 SpringBoot 版本為 2.2.4.RELEASE,其 Spring 版本為 5.2.3.RELEASE
前言
書接上文,BeanDefinition注冊(cè)到IoC容器后,緊接著就是要使用Bean了,要使用必須先要獲取Bean,這里我們就以DefaultListableBeanFactory#getBean方法來引出本次討論的內(nèi)容:BeanDefinition的合并
通過前面的章節(jié)我們了解到了BeanDefinition,那什么是BeanDefinition的合并呢?為什么要進(jìn)行合并呢? 帶著這個(gè)問題,我們到源碼中去找找答案。
為了使源碼邏輯有個(gè)參照,這里先給出一個(gè)案例,在分析源碼時(shí) 將這個(gè)案例也代入進(jìn)去方便我們理解源碼
BeanDefinition的合并源碼分析
實(shí)體類
@Data
public class SuperUser implements Serializable {
private String address;
public SuperUser(String address) {
this.address = address;
}
public SuperUser() {
}
}
@Data
@ToString(callSuper = true)
public class User extends SuperUser {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
基于GenericBeanDefinition注冊(cè)有層次的Bean
public class GenericBeanDefinitionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//父BeanDefinition
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(SuperUser.class);
//設(shè)置參數(shù)
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("address", "地址");
rootBeanDefinition.setPropertyValues(propertyValues);
//子BeanDefinition
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setBeanClass(User.class);
//設(shè)置構(gòu)造參數(shù)
ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
argumentValues.addIndexedArgumentValue(0, "我就是我");
argumentValues.addIndexedArgumentValue(1, 18);
childBeanDefinition.setConstructorArgumentValues(argumentValues);
childBeanDefinition.setParentName("superUser");
//類型相同時(shí) 以子類為主
childBeanDefinition.setPrimary(true);
context.registerBeanDefinition("superUser", rootBeanDefinition);
context.registerBeanDefinition("user", childBeanDefinition);
context.refresh();
User user = context.getBean("user", User.class);
System.out.println(user);
SuperUser superUser = context.getBean("superUser", SuperUser.class);
System.out.println(superUser);
context.close();
}
}
在分析源碼時(shí)我們要有側(cè)重點(diǎn),這里會(huì)將不太相關(guān)的邏輯一帶而過。
AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//將name解析為beanName,如果傳入的是alias,根據(jù)aliasMap進(jìn)行轉(zhuǎn)換,我們?cè)谇懊娼榻B過了
final String beanName = transformedBeanName(name);
Object bean;
// 如果是單例Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//省略日志輸出
// 這里的邏輯是根據(jù)beanName判斷是否為FactoryBea,并采用相應(yīng)方式去處理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//如果不是單例對(duì)象
else {
// 對(duì)原型對(duì)象進(jìn)行驗(yàn)證,如果當(dāng)前beanName已經(jīng)在創(chuàng)建中了 拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 獲取父BeanFactory,前面我們介紹過了 BeanFactory允許有層級(jí),可已存在父BeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果存在父BeanFactory 去父BeanFactory中查找bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//省略去父BeanFactory查找Bean的過程
}
//typeCheckOnly默認(rèn)為false ,這里將beanName放到alreadyCreated集合中 表示該Bean正在創(chuàng)建中
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 這里來到了我們要重點(diǎn)關(guān)注的地方了,bd的合并 ??
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//如果存在依賴Bean,需要進(jìn)行依賴查找
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 省略dependsOn 依賴查找代碼
}
// 這里的if..else if .. else 是根據(jù)scope取值來的
//scope=singleton時(shí)
if (mbd.isSingleton()) {
//省略單實(shí)例Bean創(chuàng)建過程
}
//scope=prototype時(shí)
else if (mbd.isPrototype()) {
//省略Prototype Bean創(chuàng)建過程
}
//scope=request、application、session時(shí)
else {
// 省略其他Scope Bean的創(chuàng)建過程
}
if (requiredType != null && !requiredType.isInstance(bean)) {
//省略類型轉(zhuǎn)換代碼
}
// 返回創(chuàng)建的Bean
return (T) bean;
}
上面的方法實(shí)現(xiàn)比較長、比較復(fù)雜,這里只對(duì)重要的地方進(jìn)行些注釋說明并將與本次討論無關(guān)的代碼先行進(jìn)行注釋。
下面就進(jìn)入到BeanDefinition的合并邏輯了
//假設(shè)beanName=user
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 檢查緩存,若存在直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
//getBeanDefinition(beanName)==>實(shí)際上去DefaultListableBeanFactory.beanDefinitionMap中根據(jù)key查找BeanDefinition,這在注冊(cè)階段已經(jīng)放到beanDefinitionMap了。
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
return getMergedBeanDefinition(beanName, bd, null);
}
//根據(jù)上面的舉例可知beanName=user,bd是User類的BeanDefinition,containingBd=null
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// 嘗試從緩存中拿
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null) {
//如果當(dāng)前BeanDefinition沒有指定parentName,說明其不存在父BeanDefinition,不需要合并。以RootBeanDefinition形式展現(xiàn)
if (bd.getParentName() == null) {
// 如果bd是RootBeanDefinition類型,直接類型轉(zhuǎn)換
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
//通過bd屬性構(gòu)造RootBeanDefinition
mbd = new RootBeanDefinition(bd);
}
}
else {
// 走到這里說明存在parentName,當(dāng)前bd需要與其父bd合并
BeanDefinition pbd;
try {
//得到父BeanName
String parentBeanName = transformedBeanName(bd.getParentName());
//!beanName.equals(parentBeanName) 條件成立 說明當(dāng)前beanName屬于子bd
if (!beanName.equals(parentBeanName)) {
//遞歸地以父bd名稱 查找父BeanDefinition。之所以遞歸地查找,是因?yàn)?可能此時(shí)的parentBeanName還有父,實(shí)體類存在多重繼承關(guān)系
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
//走到這里,說明beanName.equals(parentBeanName),很有可能是父bd查找BeanDefinition時(shí)走來的。
//獲取父BeanFactory,BeanFactory也是有層次的,有父子關(guān)系的,可參見ConfigurableBeanFactory#setParentBeanFactory
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// pbd是父BeanDefinition,由其構(gòu)造為RootBeanDefinition
mbd = new RootBeanDefinition(pbd);
//bd是子BeanDefinition,主要是繼承父類的屬性,并覆蓋與父類同名的屬性,有興趣的可以看一下overrideFrom方法實(shí)現(xiàn)
mbd.overrideFrom(bd);
}
// 如果父bd未指定scope,則設(shè)置默認(rèn)值
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}
//由于containingBd=null 這里就不看了
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
//最終返回根據(jù)當(dāng)前beanName找到的bd
return mbd;
}
}
分析了上面的源碼,我們?cè)囍偨Y(jié)一下:
1、如果不存在parentName,即不需要被合并,直接將bd轉(zhuǎn)為RootBeanDefinition 返回即可
2、如果存在parentName
- 先根據(jù)parentName 找到父bd,若實(shí)體存在多級(jí)繼承關(guān)系,則需要遞歸地查找。
- 將父bd轉(zhuǎn)為RootBeanDefinition,并將子bd與父bd進(jìn)行合并
- 設(shè)置一些其他屬性
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Windows同時(shí)配置兩個(gè)jdk環(huán)境變量的操作步驟
Java Development Kit (JDK) 是開發(fā)Java應(yīng)用程序的基礎(chǔ),包含了編譯器、調(diào)試器以及其他必要的工具,本指南將一步步指導(dǎo)您完成在Windows操作系統(tǒng)上同時(shí)配置兩個(gè)jdk環(huán)境變量的操作步驟,需要的朋友可以參考下2024-09-09
java 實(shí)現(xiàn)通過 post 方式提交json參數(shù)操作
這篇文章主要介紹了java 實(shí)現(xiàn)通過 post 方式提交json參數(shù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Java錯(cuò)誤:進(jìn)行語法分析時(shí)已到達(dá)文件結(jié)尾的解決
這篇文章主要介紹了Java錯(cuò)誤:進(jìn)行語法分析時(shí)已到達(dá)文件結(jié)尾的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Spring解決循環(huán)依賴問題的三種方法小結(jié)
在 Spring 中,循環(huán)依賴問題指的是兩個(gè)或多個(gè) bean 之間相互依賴形成的閉環(huán),具體而言,當(dāng) bean A 依賴于 bean B,同時(shí) bean B 也依賴于 bean A,就形成了循環(huán)依賴,本文就給大家介紹了Spring解決循環(huán)依賴問題的三種方法,需要的朋友可以參考下2023-09-09
詳解springMVC兩種方式實(shí)現(xiàn)多文件上傳及效率比較
本篇文章介紹了springMVC兩種方式實(shí)現(xiàn)多文件上傳及效率比較。springMVC實(shí)現(xiàn)多文件上傳有兩種,一種是字節(jié)流的方式進(jìn)行文件上傳,另外一種是使用springMVC包裝好的解析器進(jìn)行上傳,有興趣的可以了解一下。2016-12-12

