Spring中常見的7種BeanDefinition詳解
今天我們繼續(xù)來看 Spring 源碼中一個非常重要的概念:BeanDefinition。
1.BeanDefinition
在 Spring 容器中,我們廣泛使用的是一個一個的 Bean,BeanDefinition 從名字上就可以看出是關(guān)于 Bean 的定義。
事實上就是這樣,我們在 XML 文件中配置的 Bean 的各種屬性,亦或者用注解定義出來的 Bean 的各種屬性,在真正生成 Bean 直接,我們需要先對這些設(shè)置的屬性進(jìn)行解析,解析的結(jié)果需要有一個對象來承載,很明顯,這個對象就是 BeanDefinition。
無論是通過 XML 中定義的 Bean 屬性還是通過 Java 代碼定義的 Bean 屬性,都會先加載到 BeanDefinition 上,然后通過 BeanDefinition 來生成一個 Bean,從這個角度來說,BeanDefinition 和 Bean 的關(guān)系有點類似于類和對象的關(guān)系,BeanDefinition 是模板,Bean 是模板具體化之后的產(chǎn)物。
要理解 BeanDefinition,我們從 BeanDefinition 的繼承關(guān)系開始看起。
BeanDefinition 是一個接口,繼承自 BeanMetadataElement 和 AttributeAccessor 接口。
- BeanMetadataElement:該接口只有一個方法 getSource,該方法返回 Bean 的來源。
- AttributeAccessor:該接口主要規(guī)范了問任意對象元數(shù)據(jù)的方法。
我們來看下 AttributeAccessor:
public?interface?AttributeAccessor?{ ?void?setAttribute(String?name,?@Nullable?Object?value); ?@Nullable ?Object?getAttribute(String?name); ?@Nullable ?Object?removeAttribute(String?name); ?boolean?hasAttribute(String?name); ?String[]?attributeNames(); }
這里定義了元數(shù)據(jù)的訪問接口,具體的實現(xiàn)則是 AttributeAccessorSupport,這些數(shù)據(jù)采用 LinkedHashMap 進(jìn)行存儲。
這是 BeanDefinition 所繼承的兩個接口。接下來我們來看下 BeanDefinition 接口:
public?interface?BeanDefinition?extends?AttributeAccessor,?BeanMetadataElement?{ ?String?SCOPE_SINGLETON?=?ConfigurableBeanFactory.SCOPE_SINGLETON; ?String?SCOPE_PROTOTYPE?=?ConfigurableBeanFactory.SCOPE_PROTOTYPE; ?int?ROLE_APPLICATION?=?0; ?int?ROLE_SUPPORT?=?1; ?int?ROLE_INFRASTRUCTURE?=?2; ?void?setParentName(@Nullable?String?parentName); ?@Nullable ?String?getParentName(); ?void?setBeanClassName(@Nullable?String?beanClassName); ?@Nullable ?String?getBeanClassName(); ?void?setScope(@Nullable?String?scope); ?@Nullable ?String?getScope(); ?void?setLazyInit(boolean?lazyInit); ?boolean?isLazyInit(); ?void?setDependsOn(@Nullable?String...?dependsOn); ?@Nullable ?String[]?getDependsOn(); ?void?setAutowireCandidate(boolean?autowireCandidate); ?boolean?isAutowireCandidate(); ?void?setPrimary(boolean?primary); ?boolean?isPrimary(); ?void?setFactoryBeanName(@Nullable?String?factoryBeanName); ?@Nullable ?String?getFactoryBeanName(); ?void?setFactoryMethodName(@Nullable?String?factoryMethodName); ?@Nullable ?String?getFactoryMethodName(); ?ConstructorArgumentValues?getConstructorArgumentValues(); ?default?boolean?hasConstructorArgumentValues()?{ ??return?!getConstructorArgumentValues().isEmpty(); ?} ?MutablePropertyValues?getPropertyValues(); ?default?boolean?hasPropertyValues()?{ ??return?!getPropertyValues().isEmpty(); ?} ?void?setInitMethodName(@Nullable?String?initMethodName); ?@Nullable ?String?getInitMethodName(); ?void?setDestroyMethodName(@Nullable?String?destroyMethodName); ?@Nullable ?String?getDestroyMethodName(); ?void?setRole(int?role); ?int?getRole(); ?void?setDescription(@Nullable?String?description); ?@Nullable ?String?getDescription(); ?ResolvableType?getResolvableType(); ?boolean?isSingleton(); ?boolean?isPrototype(); ?boolean?isAbstract(); ?@Nullable ?String?getResourceDescription(); ?@Nullable ?BeanDefinition?getOriginatingBeanDefinition(); }
BeanDefinition 中的方法雖然多,但是結(jié)合我們平時在 XML/Java 中的配置,這些方法其實都很好理解:
- 首先一開始定義了兩個變量用來描述 Bean 是不是單例的,后面的 setScope/getScope 方法可以用來修改/獲取 scope 屬性。
- ROLE_xxx 用來描述一個 Bean 的角色,ROLE_APPLICATION 表示這個 Bean 是用戶自己定義的 Bean;ROLE_SUPPORT 表示這個 Bean 是某些復(fù)雜配置的支撐部分;ROLE_INFRASTRUCTURE 表示這是一個 Spring 內(nèi)部的 Bean,通過 setRole/getRole 可以修改。
- setParentName/getParentName 用來配置 parent 的名稱,這塊可能有的小伙伴使用較少,這個對應(yīng)著 XML 中的
<bean parent="">
配置,在之前的視頻中松哥已經(jīng)和大家講過了 Spring 中 parent 的使用了。 - setBeanClassName/getBeanClassName 這個就是配置 Bean 的 Class 全路徑,對應(yīng) XML 中的
<bean class="">
配置。 - setLazyInit/isLazyInit 配置/獲取 Bean 是否懶加載,這個對應(yīng)了 XML 中的
<bean lazy-init="">
配置。 - setDependsOn/getDependsOn 配置/獲取 Bean 的依賴對象,這個對應(yīng)了 XML 中的
<bean depends-on="">
配置。 - setAutowireCandidate/isAutowireCandidate 配置/獲取 Bean 是否是自動裝配,對應(yīng)了 XML 中的
<bean autowire-candidate="">
配置。 - setPrimary/isPrimary 配置/獲取當(dāng)前 Bean 是否為首選的 Bean,對應(yīng)了 XML 中的
<bean primary="">
配置。 - setFactoryBeanName/getFactoryBeanName 配置/獲取 FactoryBean 的名字,對應(yīng)了 XML 中的
<bean factory-bean="">
配置。 - setFactoryMethodName/getFactoryMethodName 和上一條成對出現(xiàn)的,對應(yīng)了 XML 中的
<bean factory-method="">
配置,不再贅述。 - getConstructorArgumentValues 返回該 Bean 構(gòu)造方法的參數(shù)值。
- hasConstructorArgumentValues 判斷上一條是否是空對象。
- getPropertyValues 這個是獲取普通屬性的集合。
- hasPropertyValues 判斷上一條是否為空對象。
- setInitMethodName/setDestroyMethodName 配置 Bean 的初始化方法、銷毀方法。
- setDescription/getDescription 配置/返回 Bean 的描述。
- isSingleton Bean 是否為單例。
- isPrototype Bean 是否為原型。
- isAbstract Bean 是否抽象。
- getResourceDescription 返回定義 Bean 的資源描述。
- getOriginatingBeanDefinition 如果當(dāng)前 BeanDefinition 是一個代理對象,那么該方法可以用來返回原始的 BeanDefinition 。
這個就是 BeanDefinition 的定義以及它里邊方法的含義。
2.BeanDefinition 實現(xiàn)類
上面只是 BeanDefinition 接口的定義,BeanDefinition 還擁有諸多實現(xiàn)類,我們也來大致了解下。
先來看一張繼承關(guān)系圖:
這么多實現(xiàn)類看著有點眼花繚亂,不過搞清楚了每一個接口和類的作用,再看就很容易了。
2.1 AbstractBeanDefinition
AbstractBeanDefinition 是一個抽象類,它根據(jù) BeanDefinition 中定義的接口提供了相應(yīng)的屬性,并實現(xiàn)了 BeanDefinition 中定義的一部分方法。BeanDefinition 中原本只是定義了一系列的 get/set 方法,并沒有提供對應(yīng)的屬性,在 AbstractBeanDefinition 中將所有的屬性定義出來了。
后面其他的實現(xiàn)類也基本上都是在 AbstractBeanDefinition 的基礎(chǔ)上完成的。
2.2 RootBeanDefinition
這是一個比較常用的實現(xiàn)類,對應(yīng)了一般的元素標(biāo)簽。
2.3 ChildBeanDefinition
可以讓子 BeanDefinition 定義擁有從父 BeanDefinition 那里繼承配置的能力,如果子 Bean 從父 Bean 獲取配置,可以參考松哥之前的這篇文章:Spring BeanDefinition 也分父子?。
2.4 GenericBeanDefinition
GenericBeanDefinition 是從 Spring2.5 以后新加入的 BeanDefinition 實現(xiàn)類。GenericBeanDefinition 可以動態(tài)設(shè)置父 Bean,同時兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能,因此,自從有了 GenericBeanDefinition 之后,RootBeanDefinition 和 ChildBeanDefinition 現(xiàn)在相對就用的少了。
2.5 AnnotatedBeanDefinition
這個表示注解類型 BeanDefinition,用于表示通過注解配置的 Bean 定義。通過 AnnotatedBeanDefinition,我們可以獲取到被注解的 Bean 的相關(guān)信息,包括注解類型、屬性值、方法等。這個接口提供了一種方便的方式來處理通過注解方式配置的 Bean,并且可以在運行時動態(tài)地獲取和操作這些注解信息,當(dāng)然,這是一個接口,它有三個實現(xiàn)類,分別是 AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition。
2.6 AnnotatedGenericBeanDefinition
使用了 @Configuration 注解標(biāo)記配置類會解析為 AnnotatedGenericBeanDefinition。
2.7 ScannedGenericBeanDefinition
這個是通過包掃描自動注冊的 Bean,就會被解析為 ScannedGenericBeanDefinition。
2.8 ConfigurationClassBeanDefinition
這是一個私有的內(nèi)部類。我們通過 @Bean 注解定義的 Bean,最終會被解析為 ConfigurationClassBeanDefinition。
2.9 ClassDerivedBeanDefinition
ClassDerivedBeanDefinition 的作用是擴(kuò)展并描述一個類派生的Bean的元數(shù)據(jù)。它是 AnnotatedBeanDefinition 接口的一個實現(xiàn)類,在 Spring 框架中用于表示通過類派生方式配置的Bean定義。
2.10 CreateFromClassBeanDefinition
這個是按照類型創(chuàng)建 Bean 的時候會用到。
差不多就這么多了,大部分我們?nèi)粘i_發(fā)中其實都用不上,接下來松哥通過幾個具體的案例來和小伙伴們演示這些 BeanDefinition 的具體用法。后面的文章,我們再來分析這些 BeanDefinition 在 Spring 源碼中是如何應(yīng)用的。
3.實踐
接下來我通過幾個具體的案例來和小伙伴們演示各種不同的 BeanDefinition 的用法,今天我主要和小伙伴們演示我們純手動使用 BeanDefinition,然后分析一下我們平時的配置本質(zhì)上使用的 BeanDefinition 是哪一個,今天我們先不去源碼分析,單純的就看看效果。
3.1 GenericBeanDefinition
先來看 GenericBeanDefinition,這個功能相對比較全,兼具 RootBeanDefinition 和 ChildBeanDefinition 的能力。
先來看一個簡單用法:
DefaultListableBeanFactory?beanFactory?=?new?DefaultListableBeanFactory(); GenericBeanDefinition?bd?=?new?GenericBeanDefinition(); bd.setBeanClass(User.class); MutablePropertyValues?pValues?=?new?MutablePropertyValues(); pValues.add("username",?"javaboy"); bd.setPropertyValues(pValues); beanFactory.registerBeanDefinition("user",?bd); User?user?=?beanFactory.getBean("user",?User.class); System.out.println("user?=?"?+?user);
小伙伴們看到,我們這里向 Spring 容器注冊了一個 GenericBeanDefinition 類型的 BeanDefinition,GenericBeanDefinition 中包含了具體的 class 以及 Bean 的各個屬性值。
如果我們在 Bean 定義的時候,想要使用繼承特性,也可以使用 GenericBeanDefinition:
DefaultListableBeanFactory?beanFactory?=?new?DefaultListableBeanFactory(); GenericBeanDefinition?parentBD?=?new?GenericBeanDefinition(); GenericBeanDefinition?childBD?=?new?GenericBeanDefinition(); parentBD.setBeanClass(Animal.class); MutablePropertyValues?pValues?=?new?MutablePropertyValues(); pValues.add("name",?"小黃"); parentBD.setPropertyValues(pValues); childBD.setBeanClass(Dog.class); childBD.setParentName("animal"); beanFactory.registerBeanDefinition("animal",?parentBD); beanFactory.registerBeanDefinition("dog",?childBD); Dog?dog?=?beanFactory.getBean("dog",?Dog.class); System.out.println("dog?=?"?+?dog);
這次我直接定義了兩個 GenericBeanDefinition,一個作為 parent,另外一個作為 child,為 child 設(shè)置 parentName,則 child 可以繼承 parent 中的屬性。上面的案例中,最終打印出來 dog 的 name 屬性就是 小黃,這塊小伙伴們可以參考松哥之前的文章:Spring BeanDefinition 也分父子?。
我們平時通過 XML 文件定義的 Bean,最終解析后就是 GenericBeanDefinition。
例如下面這樣:
<?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?http://www.springframework.org/schema/beans/spring-beans.xsd"> ????<bean?class="org.javaboy.demo.User"?id="user"></bean> </beans>
加載 XML 文件,創(chuàng)建容器:
ClassPathXmlApplicationContext?ctx?=?new?ClassPathXmlApplicationContext("beans.xml"); String[]?beanDefinitionNames?=?ctx.getBeanDefinitionNames(); for?(String?beanDefinitionName?:?beanDefinitionNames)?{ ????BeanDefinition?bd?=?ctx.getBeanFactory().getBeanDefinition(beanDefinitionName); ????System.out.println(beanDefinitionName?+?">>>"?+?bd.getClass()); }
最終打印結(jié)果如下:
這個也好理解,畢竟我們在 XML 中配置的時候,可能存在 parent,也可能不存在,用 GenericBeanDefinition 就能夠應(yīng)對各種情況。
3.2 RootBeanDefinition/ChildBeanDefinition
這兩個常規(guī)的功能其實都有,但是 RootBeanDefinition 一般可以用來做 parent,不能用作 child,即給 RootBeanDefinition 不能配置 parentName 屬性。強(qiáng)行設(shè)置會拋出如下異常:
@Override public?void?setParentName(@Nullable?String?parentName)?{ ?if?(parentName?!=?null)?{ ??throw?new?IllegalArgumentException("Root?bean?cannot?be?changed?into?a?child?bean?with?parent?reference"); ?} }
ChildBeanDefinition 則既可以做 parent 也可以做 child,但是 ChildBeanDefinition 在使用的使用必須指定 parent,即使 ChildBeanDefinition 作為 parent,也必須指定 parent,所以 ChildBeanDefinition 在使用的過程中有一點點局限性,因此目前被 GenericBeanDefinition 代替了。
來看一個簡單的案例:
DefaultListableBeanFactory?beanFactory?=?new?DefaultListableBeanFactory(); RootBeanDefinition?parentBD?=?new?RootBeanDefinition(); parentBD.setBeanClass(Animal.class); MutablePropertyValues?pValues?=?new?MutablePropertyValues(); pValues.add("name",?"小黃"); parentBD.setPropertyValues(pValues); ChildBeanDefinition?childBD?=?new?ChildBeanDefinition("animal"); childBD.setBeanClass(Dog.class); beanFactory.registerBeanDefinition("animal",?parentBD); beanFactory.registerBeanDefinition("dog",?childBD); Dog?dog?=?beanFactory.getBean("dog",?Dog.class); System.out.println("dog?=?"?+?dog);
3.3 AnnotatedGenericBeanDefinition
對于使用 @Configuration 注解標(biāo)記的類,最終解析出來的 BeanDefinition 就是 AnnotatedGenericBeanDefinition。例如我有一個配置類如下:
@Configuration public?class?JavaConfig?{ }
加載配置類并啟動容器:
AnnotationConfigApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(JavaConfig.class); String[]?beanDefinitionNames?=?ctx.getBeanDefinitionNames(); for?(String?beanDefinitionName?:?beanDefinitionNames)?{ ????BeanDefinition?beanDefinition?=?ctx.getBeanFactory().getBeanDefinition(beanDefinitionName); ????System.out.println(beanDefinitionName?+?"?>>>?"?+?beanDefinition.getClass()); }
最終打印結(jié)果如下:
3.4 ScannedGenericBeanDefinition
這個是那些通過包掃描注冊到 Spring 容器中的 Bean,在一開始定義出來的 BeanDefinition 就是 ScannedGenericBeanDefinition。
例如我有如下 Bean:
@Service public?class?UserService?{ }
然后配置包掃描:
@Configuration @ComponentScan public?class?JavaConfig?{ }
啟動容器:
AnnotationConfigApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(JavaConfig.class); String[]?beanDefinitionNames?=?ctx.getBeanDefinitionNames(); for?(String?beanDefinitionName?:?beanDefinitionNames)?{ ????BeanDefinition?beanDefinition?=?ctx.getBeanFactory().getBeanDefinition(beanDefinitionName); ????System.out.println(beanDefinitionName?+?"?>>>?"?+?beanDefinition.getClass()); }
最終打印結(jié)果如下:
3.5 ConfigurationClassBeanDefinition
當(dāng)我們通過 @Bean 注解去定義 Bean 的時候,那么被 @Bean 注解標(biāo)記的類就會被解析為 ConfigurationClassBeanDefinition。
例如下面這個例子:
@Configuration public?class?JavaConfig?{ ????@Bean ????User?user()?{ ????????return?new?User(); ????} }
查看解析后的 BeanDefinition 如下:
3.6 CreateFromClassBeanDefinition
這個其實用的少,但是咱么既然講到 Spring,松哥也說兩句。
這個是當(dāng)我們想要創(chuàng)建一個對象,我們希望這個對象能夠自動走一遍 Spring 中的各種后置處理器的時候,那么可以調(diào)用 createBean 方法,該方法內(nèi)部使用了 CreateFromClassBeanDefinition。
例如如下代碼:
AnnotationConfigApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(JavaConfig.class); ConfigurableListableBeanFactory?beanFactory?=?ctx.getBeanFactory(); User?user?=?beanFactory.createBean(User.class); System.out.println("user?=?"?+?user);
使用這種方式去創(chuàng)建一個 Bean,這個 Bean 會走一遍 Spring 中 Bean 的后置處理器,其中,createBean 方法的內(nèi)部就使用了 CreateFromClassBeanDefinition。
3.7 ClassDerivedBeanDefinition
ClassDerivedBeanDefinition 和 CreateFromClassBeanDefinition 其實比較像,差別在于二者處理構(gòu)造方法的方式不同。
而且 ClassDerivedBeanDefinition 是一個相當(dāng)冷門的 BeanDefinition,在 GenericApplicationContext 的實現(xiàn)類中,可以使用 GenericXmlApplicationContext、StaticApplicationContext 或者 GenericGroovyApplicationContext,只有這三個類中 registerBean 方法用到了 ClassDerivedBeanDefinition,我們常見的 AnnotationConfigApplicationContext 由于方法重寫的緣故并未使用 ClassDerivedBeanDefinition。
StaticApplicationContext?ctx?=?new?StaticApplicationContext(); ctx.registerBean(User.class,()->{ ????User?user?=?new?User(); ????user.setUsername("javaboy"); ????return?user; }); User?user?=?ctx.getBean(User.class); System.out.println("user?=?"?+?user); String[]?beanDefinitionNames?=?ctx.getBeanDefinitionNames(); for?(String?beanDefinitionName?:?beanDefinitionNames)?{ ????BeanDefinition?bd?=?ctx.getBeanDefinition(beanDefinitionName); ????System.out.println(beanDefinitionName?+?">>>"?+?bd.getClass()); }
我們可以調(diào)用 registerBean 方法向 Spring 容器中注入一個 Bean,該方法第二個參數(shù)是一個 Bean 的生產(chǎn)者,如果不指定生產(chǎn)者,那么這個方法最終就是通過第一個參數(shù)反射創(chuàng)建 Bean,registerBean 方法的內(nèi)部就是使用了 ClassDerivedBeanDefinition。
好啦,BeanDefinition 一共就是這七種,接下來我會通過幾篇文章和大家重點介紹 GenericBeanDefinition、AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition 這四種最為常見的 BeanDefinition。
以上就是Spring中常見的7種BeanDefinition詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring BeanDefinition的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之哈希算法實現(xiàn)
哈希表本質(zhì)是一種(key,value)結(jié)構(gòu),由此我們可以聯(lián)想到,能不能把哈希表的key映射成數(shù)組的索引index呢?如果這樣做的話那么查詢相當(dāng)于直接查詢索引,查詢時間復(fù)雜度為O(1),其實這也正是當(dāng)key為int型時的做法,將key通過某種做法映射成index,從而轉(zhuǎn)換成數(shù)組結(jié)構(gòu)2022-02-02maven倉庫中心mirrors配置多個下載中心(執(zhí)行最快的鏡像)
這篇文章主要介紹了maven倉庫中心mirrors配置多個下載中心(執(zhí)行最快的鏡像),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07面試題:java中為什么foreach中不允許對元素進(jìn)行add和remove
讀者遇到了一個比較經(jīng)典的面試題,也就是標(biāo)題上說的,為什么 foreach 中不允許對元素進(jìn)行 add 和 remove,本文就詳細(xì)的介紹一下,感興趣的可以了解一下2021-10-10