Spring官網閱讀(四)BeanDefinition(上)

點擊上方藍字java

給一個關注吧程序員


前面幾篇文章已經學習了官網中的1.2,1.3,1.4三小結,主要是容器,Bean的實例化及Bean之間的依賴關係等。這篇文章,咱們繼續官網的學習,主要是BeanDefinition的相關知識,這是Spring中很是基礎的一塊內容,也是咱們閱讀源碼的基石。本文主要涉及到官網中的1.31.5中的一些補充知識。同時爲咱們1.7小節中BeanDefinition的合併作一些鋪墊web

  • 1.BeanDefinition繼承的接口spring

  • 2.AbstractBeanDefinition微信

    • AbstractBeanDefinition的繼承關係數據結構

    • 爲何須要AbstractBeanDefinition?編輯器

  • 3.AbstractBeanDefinition的三個子類ide

    • GenericBeanDefinition函數

    • ChildBeanDefinition學習

    • RootBeanDefinition

    • 4.AnnotatedBeanDefinition

    • 5.AnnotatedBeanDefinition的三個實現類

  • 1.5小結內容的補充

    • 單例

    • 原型

BeanDefinition是什麼?

咱們先看官網上是怎麼解釋的:

在這裏插入圖片描述

從上文中,咱們能夠得出如下幾點結論:

  1. BeanDefinition包含了咱們對bean作的配置,好比XML <bean/>標籤的形式進行的配置
  2. 換而言之,Spring將咱們對bean的定義信息進行了抽象,抽象後的實體就是 BeanDefinition, 而且Spring會以此做爲標準來對Bean進行建立
  3. BeanDefinition包含如下元數據:
    • 一個全限定類名,一般來講,就是對應的bean的全限定類名。
    • bean的行爲配置元素,這些元素展現了這個bean在容器中是如何工做的包括 scope(域,咱們文末有簡單介紹), lifecycle callbacks(生命週期回調,下篇文章介紹)等等
    • 這個bean的依賴信息
    • 一些其餘配置信息,好比咱們配置了一個鏈接池對象,那麼咱們還會配置它的池子大小,最大鏈接數等等

在這裏,咱們來比較下,正常的建立一個bean,跟Spring經過抽象出一個BeanDefinition來建立bean有什麼區別:

正常的建立一個java bean:

在這裏插入圖片描述

Spring經過BeanDefinition來建立bean:

在這裏插入圖片描述

經過上面的比較,咱們能夠發現,相比於正常的對象的建立過程,Spring對其管理的bean沒有直接採用new的方式,而是先經過解析配置數據以及根據對象自己的一些定義而獲取其對應的beandefinition,並將這個beandefinition做爲以後建立這個bean的依據。同時Spring在這個過程當中提供了一些擴展點,例如咱們在圖中所提到了BeanfactoryProcessor。這些你們先做爲了解,以後在源碼階段咱們再分析。

BeanDefinition的方法分析

這裏對於每一個字段我只保留了一個方法,只要知道了字段的含義,方法的含義咱們天然就知道了

// 獲取父BeanDefinition,主要用於合併,下節中會詳細分析
String getParentName();

// 對於的bean的ClassName
void setBeanClassName(@Nullable String beanClassName);

// Bean的做用域,不考慮web容器,主要兩種,單例/原型,見官網中1.5內容
void setScope(@Nullable String scope);

// 是否進行懶加載
void setLazyInit(boolean lazyInit);

// 是否須要等待指定的bean建立完以後再建立
void setDependsOn(@Nullable String... dependsOn);

// 是否做爲自動注入的候選對象
void setAutowireCandidate(boolean autowireCandidate);

// 是否做爲主選的bean
void setPrimary(boolean primary);

// 建立這個bean的類的名稱
void setFactoryBeanName(@Nullable String factoryBeanName);

// 建立這個bean的方法的名稱
void setFactoryMethodName(@Nullable String factoryMethodName);

// 構造函數的參數
ConstructorArgumentValues getConstructorArgumentValues();

// setter方法的參數
MutablePropertyValues getPropertyValues();

// 生命週期回調方法,在bean完成屬性注入後調用
void setInitMethodName(@Nullable String initMethodName);

// 生命週期回調方法,在bean被銷燬時調用
void setDestroyMethodName(@Nullable String destroyMethodName);

// Spring能夠對bd設置不一樣的角色,瞭解便可,不重要
// 用戶定義 int ROLE_APPLICATION = 0;
// 某些複雜的配置    int ROLE_SUPPORT = 1;
// 徹底內部使用   int ROLE_INFRASTRUCTURE = 2;
void setRole(int role);

// bean的描述,沒有什麼實際含義
void setDescription(@Nullable String description);

// 根據scope判斷是不是單例
boolean isSingleton();

// 根據scope判斷是不是原型
boolean isPrototype();

// 跟合併beanDefinition相關,若是是abstract,說明會被做爲一個父beanDefinition,不用提供class屬性
boolean isAbstract();

// bean的源描述,沒有什麼實際含義 
String getResourceDescription();

// cglib代理前的BeanDefinition
BeanDefinition getOriginatingBeanDefinition();

BeanDefinition的繼承關係

類圖以下:

在這裏插入圖片描述

1.BeanDefinition繼承的接口

  • org.springframework.core.AttributeAccessor

先來看接口上標註的這段java doc

Interface defining a generic contract for attaching and accessing metadata to/from arbitrary objects.

翻譯下來就是:

這個接口爲從其它任意類中獲取或設置元數據提供了一個通用的規範。

其實這就是訪問者模式的一種體現,採用這方方法,咱們能夠將數據接口操做方法進行分離。

咱們再來看這個接口中定義的方法:

void setAttribute(String name, @Nullable Object value);

Object getAttribute(String name);

Object removeAttribute(String name);

boolean hasAttribute(String name);

String[] attributeNames();

就是提供了一個獲取屬性跟設置屬性的方法

那麼如今問題來了,在咱們整個BeanDefiniton體系中,這個被操做的數據結構在哪呢?不要急,在後文中的AbstractBeanDefinition會介紹。

  • org.springframework.beans.BeanMetadataElement

咱們仍是先看java doc:

Interface to be implemented by bean metadata elements that carry a configuration source object.

翻譯:這個接口提供了一個方法去獲取配置源對象,其實就是咱們的原文件。

這個接口只提供了一個方法:

@Nullable
Object getSource();

咱們能夠理解爲,當咱們經過註解的方式定義了一個IndexService時,那麼此時的IndexService對應的BeanDefinition經過getSource方法返回的就是IndexService.class這個文件對應的一個File對象。

若是咱們經過@Bean方式定義了一個IndexService的話,那麼此時的source是被@Bean註解所標註的一個Mehthod對象。

2.AbstractBeanDefinition

AbstractBeanDefinition的繼承關係

先看一下類圖:

在這裏插入圖片描述
  • org.springframework.core.AttributeAccessorSupport

能夠看到這個類實現了AttributeAccerror接口,咱們在上文中已經提到過,AttributeAccerror採用了訪問者的涉及模式,將數據結構操做方法進行了分離,數據結構在哪呢?就在AttributeAccessorSupport這個類中,咱們看下它的代碼:

public abstract class AttributeAccessorSupport implements AttributeAccessorSerializable {
 /** Map with String keys and Object values. */
 private final Map<String, Object> attributes = new LinkedHashMap<>();

    @Override
 public void setAttribute(String name, @Nullable Object value) {
  Assert.notNull(name, "Name must not be null");
  if (value != null) {
   this.attributes.put(name, value);
  }
  else {
   removeAttribute(name);
  }
 }
 ......省略下面的代碼

能夠看到,在這個類中,維護了一個map,這就是BeanDefinition體系中,經過訪問者模式全部操做的數據對象。

  • org.springframework.beans.BeanMetadataAttributeAccessor

這個類主要就是對咱們上面的map中的數據操做作了更深一層的封裝,咱們就看其中的兩個方法:

public void addMetadataAttribute(BeanMetadataAttribute attribute) {
    super.setAttribute(attribute.getName(), attribute);
}
public BeanMetadataAttribute getMetadataAttribute(String name) {
    return (BeanMetadataAttribute) super.getAttribute(name);
}

能夠發現,它只是將屬性統一封裝成了一個BeanMetadataAttribute,而後就調用了父類的方法,將其放入到map中。

咱們的AbstractBeanDefinition經過繼承了BeanMetadataAttributeAccessor這個類,能夠對BeanDefinition中的屬性進行操做。這裏說的屬性僅僅指的是BeanDefinition中的一個map,而不是它的其它字段。

爲何須要AbstractBeanDefinition?

對比BeanDefinition的源碼咱們能夠發現,AbstractBeanDefinitionBeanDefinition的大部分方法作了實現(沒有實現parentName相關方法)。同時定義了一系列的常量及默認字段。這是由於BeanDefinition接口過於頂層,若是咱們依賴BeanDefinition這個接口直接去建立其實現類的話過於麻煩,因此經過AbstractBeanDefinition作了一個下沉,並給不少屬性賦了默認值,例如:

// 默認狀況不是懶加載的
private boolean lazyInit = false;
// 默認狀況不採用自動注入
private int autowireMode = AUTOWIRE_NO;
// 默認狀況做爲自動注入的候選bean
private boolean autowireCandidate = true;
// 默認狀況不做爲優先使用的bean
private boolean primary = false;
........

這樣能夠方便咱們建立其子類,如咱們接下來要講的:ChildBeanDefinition,RootBeanDefinition等等

3.AbstractBeanDefinition的三個子類

GenericBeanDefinition

  • 替代了原來的 ChildBeanDefinition,比起 ChildBeanDefinition更爲靈活, ChildBeanDefinition在實例化的時候必需要指定一個 parentName,而 GenericBeanDefinition不須要。咱們經過註解配置的bean以及咱們的配置類(除 @Bena外)的 BeanDefiniton類型都是 GenericBeanDefinition

ChildBeanDefinition

  • 如今已經被 GenericBeanDefinition所替代了。我在 5.1.x版本沒有找到使用這個類的代碼。

RootBeanDefinition

  • Spring在啓動時會實例化幾個初始化的 BeanDefinition,這幾個 BeanDefinition的類型都爲 RootBeanDefinition
  • Spring在合併 BeanDefinition返回的都是 RootBeanDefinition
  • 咱們經過 @Bean註解配置的bean,解析出來的 BeanDefinition都是 RootBeanDefinition(其實是其子類 ConfigurationClassBeanDefinition

4.AnnotatedBeanDefinition

這個接口繼承了咱們的BeanDefinition接口,咱們查看其源碼能夠發現:

AnnotationMetadata getMetadata();

@Nullable
MethodMetadata getFactoryMethodMetadata();

這個接口相比於BeanDefinition, 僅僅多提供了兩個方法

  • getMetadata(),主要用於獲取註解元素據。從接口的命名上咱們也能看出,這類主要用於保存經過註解方式定義的bean所對應的 BeanDefinition。因此它多提供了一個關於獲取註解信息的方法
    • getFactoryMethodMetadata(),這個方法跟咱們的 @Bean註解相關。當咱們在一個配置類中使用了 @Bean註解時,被 @Bean註解標記的方法,就被解析成了 FactoryMethodMetadata

5.AnnotatedBeanDefinition的三個實現類

AnnotatedGenericBeanDefinition

  • 經過形以下面的API註冊的bean都是 AnnotatedGenericBeanDefinition
public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(Config.class);
}

這裏的config對象,最後在Spring容器中就是一個AnnotatedGenericBeanDefinition

  • 經過 @Import註解導入的類,最後都是解析爲 AnnotatedGenericBeanDefinition

ScannedGenericBeanDefinition

  • 都過註解掃描的類,如 @Service, @Compent等方式配置的Bean都是 ScannedGenericBeanDefinition

ConfigurationClassBeanDefinition

  • 經過 @Bean的方式配置的Bean爲 ConfigurationClassBeanDefinition

最後,咱們還剩一個ClassDerivedBeanDefinition,這個類是跟kotlin相關的類,通常用不到,筆者也不熟,這裏就無論了!

總結

至此,咱們算完成了BeanDefinition部分的學習,在下一節中,我將繼續跟你們一塊兒學習BeanDefinition合併的相關知識。這篇文章中,主要學習了

  1. 什麼是 BeanDefinition,總結起來就是一句話,Spring建立bean時的建模對象。
  2. BeanDefinition的具體使用的子類,以及Spring在哪些地方使用到了它們。這部份內容在後面的學習中很重要,畫圖總結以下:
在這裏插入圖片描述

1.5小結內容的補充

單例

一個單例的bean意味着,這個bean只會容器建立一次。在建立後,容器中的每一個地方使用的都是同一個bean對象。這裏用Spring官網上的一個原圖:在上面圖片的例子中,accountDao在被其它三個bean引用,這三個引用指向的都是同一個bean。

在默認狀況下,Spring中bean的默認域就是單例的。分XML跟註解兩種配置方式:

<!--即便配置singleton也是單例的,這是Spring的默認配置-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
@Component
// 這裏配置singleton,默認就是singleton
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class LuBanService{

}

原型

一個原型的bean意味着,每次咱們使用時都會從新建立這個bean。

在這裏插入圖片描述

在上面圖片的例子中,accountDao在被其它三個bean引用,這三個引用指向的都是一個新建的bean。

兩種配置方式:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
@Component
// 這裏配置prototype
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LuBanService{
    
}
您點的每一個贊,我都認真當成了喜歡



本文分享自微信公衆號 - 程序員DMZ(programerDmz)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索