一起來讀官方文件-----SpringIOC(02)

mug發表於2021-09-09

1.3。Bean總覽

Spring IoC容器管理一個或多個bean。這些bean是使用您提供給容器的配置後設資料建立的(例如,以XML 定義的形式 )。

在容器本身內,這些bean定義表示為BeanDefinition物件,其中包含(除其他資訊外)以下後設資料:

包限定的類名:通常,定義了Bean的實際實現類。
Bean行為配置元素,用於宣告Bean在容器中的行為(作用域,生命週期回撥等)。
引用其他bean進行其工作所需的bean。這些引用也稱為協作者或依賴項。
要在新建立的物件中設定的其他配置設定-例如,池的大小限制或在管理連線池的Bean中使用的連線數。

該後設資料轉換為構成每個bean定義的一組屬性。下表描述了這些屬性:

屬性 解釋
例項化bean
名稱 bean名稱
生命週期 bean生命週期
建構函式引數 依賴注入
屬性 依賴注入
自動注入模式 自動注入的合作者
延遲初始化模式 懶初始化bean
初始化方法 初始化回撥
銷燬方式 銷燬回撥

除了包含有關如何建立特定bean的資訊的bean定義外,這些ApplicationContext實現還允許註冊在容器外部(由使用者)建立的現有物件。這是透過方法訪問ApplicationContext的BeanFactory的getBeanFactory()來完成,該方法返回BeanFactory的DefaultListableBeanFactory實現。DefaultListableBeanFactory 透過registerSingleton(…)和 registerBeanDefinition(…)方法支援此註冊。但是,典型的應用程式僅使用透過常規bean定義後設資料定義的bean。

自定義類  不帶任何註解
    public class LearnBean {
    	public LearnBean(String name) {
    	}
    	public String getString(){
    		return "learnSpring";
    	}
    }
    
第一種:AnnotationConfigApplicationContext自帶的registerBean方法,可以傳入class和構造引數

	AnnotationConfigApplicationContext annotationConfigApplicationContext = 
	                            new AnnotationConfigApplicationContext();
	annotationConfigApplicationContext.registerBean(LearnBean.class,"");
	annotationConfigApplicationContext.refresh();
	LearnBean bean = (LearnBean) annotationConfigApplicationContext.getBean("learnBean");
	System.out.println(bean.getString());
	
第二種:還可以傳入 class和beanDefinition也就是配置後設資料 這個和 getBeanFactory().registerBeanDefinition(..)是一個意思

    AnnotationConfigApplicationContext annotationConfigApplicationContext = 
                                new AnnotationConfigApplicationContext();
	
	RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(LearnBean.class);
	ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
	constructorArgumentValues.addIndexedArgumentValue(0,"111");
	rootBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
	annotationConfigApplicationContext.registerBeanDefinition("learnBean",rootBeanDefinition);

	annotationConfigApplicationContext.refresh();

	LearnBean bean = (LearnBean) annotationConfigApplicationContext.getBean("learnBean");
	System.out.println(bean.getString());
	
	
注意:如果不寫
	annotationConfigApplicationContext.refresh();
就會報錯
Exception in thread "main" java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@68de145 has not been refreshed yet
	at org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1096)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
	at org.springframework.example.DebuggerSpringMain.main(DebuggerSpringMain.java:40)
	
	

Bean後設資料和手動提供的單例例項需要儘早註冊,以便容器在自動裝配和其他自省步驟中正確地推理它們。雖然在某種程度上支援覆蓋現有後設資料和現有單例例項,但是在執行時(與對工廠的實時訪問同時)對新bean的註冊不被正式支援,並且可能導致併發訪問異常,bean容器中的狀態不一致或都。

很顯然註冊太晚,就無法和Spring基礎的步驟融合,一些依賴注入無法完成
1.3.1。bean的命名

每個bean具有一個或多個識別符號。這些識別符號在承載Bean的容器內必須唯一。
一個bean通常只有一個識別符號。但是,如果需要多個,則可以將多餘的別名視為別名。

在基於XML配置檔案,您可以使用id屬性,name屬性,或兩者來指定bean識別符號。id屬性使您可以精確指定一個ID。
按照慣例,這些名稱是字母數字(“myBean”,“someService”等),但它們也可以包含特殊字元。如果要為bean引入其他別名,還可以在name屬性中指定它們,並用逗號(,),分號(;)或空格分隔。
作為歷史記錄,在Spring3.1之前的版本中,該id屬性被定義為一種xsd:ID型別,該型別限制了可能的字元。從3.1開始,它被定義為xsd:string型別。
請注意,Bean id唯一性仍由容器強制執行,儘管不再由XML解析器執行。

您不需要為bean 提供name或id。如果不提供 name或id顯式提供,則容器將為該bean生成一個唯一的名稱。
但是,如果您希望透過使用ref元素或服務定位器樣式查詢透過名稱引用那個bean,那麼您必須提供一個名稱。使用內部bean 和 自動裝配合的時候 通常不需要使用名稱。

###### Bean命名約定
約定是在命名bean時將標準Java約定用於例項欄位名稱。
也就是說,bean名稱以小寫字母開頭,並從那裡用駝峰式大小寫。
這樣的名字的例子包括accountManager, accountService,userDao,loginController,等等。

一致地命名Bean使您的配置更易於閱讀和理解。
另外,如果您使用Spring AOP,則在將切點應用於名稱相關的一組bean時,它會很有幫助。

透過在類路徑中進行元件掃描,Spring會按照前面描述的規則為未命名的元件生成Bean名稱:本質上,採用簡單的類名稱並將其初始字元轉換為小寫。
但是,在(不尋常的)特殊情況下,如果有多個字元並且第一個和第二個字元均為大寫字母,則會保留原始大小寫。
這些規則與java.beans.Introspector.decapitalize(由Spring在此處使用)定義的規則相同。
在Bean定義之外別名Bean

在bean定義本身中,可以透過使用id屬性指定的最多一個名稱和屬性中任意數量的其他名稱的組合來為bean提供多個名稱name。這些名稱可以是同一個bean的等效別名,並且在某些情況下很有用,例如,透過使用特定於該元件本身的bean名稱,讓應用程式中的每個元件都引用一個公共依賴項。

但是,在實際定義bean的地方指定所有別名並不總是足夠的。有時需要為在別處定義的bean引入別名。在大型系統中通常是這種情況,在大型系統中,配置在每個子系統之間分配,每個子系統都有自己的物件定義集。在基於XML的配置後設資料中,您可以使用元素來完成此任務。以下示例顯示瞭如何執行此操作:

<alias name="fromName" alias="toName"/>
在這種情況下,(在同一個容器中)名為fromName的bean在使用了這個別名定義之後,也可以被稱為toName。

例如,
子系統A的配置後設資料可能引用一個名為subsystem-DataSource的資料來源。
子系統B的配置後設資料可以引用名為subsystembl-DataSource的資料來源。
在編寫使用這兩個子系統的主應用程式時,主應用程式以myApp-dataSource的名稱引用資料來源。
要讓這三個名稱引用同一個物件,您可以在配置後設資料中新增以下別名定義:

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

現在,每個元件和主應用程式都可以透過唯一的名稱引用資料來源,並且可以保證不與任何其他定義衝突(有效地建立名稱空間),但是它們引用的是同一bean。

Java-configuration

如果使用Java-configuration,則@Bean註解可用於提供別名。有關詳細資訊,請參見使用@Bean註解。

1.3.2。例項化bean

bean定義本質上是建立一個或多個物件的訣竅。當被請求時,容器檢視已命名bean的配方,並使用該bean定義封裝的配置後設資料來建立(或獲取)實際物件。

如果使用基於XML的配置後設資料,則可以在元素的class屬性中指定要例項化的物件的型別(或類)。此 class屬性(在內部是例項的Class屬性BeanDefinition)通常是必需的。

以下兩種情況會用到Class屬性:

通常,在容器本身透過反射呼叫其建構函式直接建立bean的情況下,
指定要構造的bean類,這在某種程度上等同於使用new運算子的Java程式碼。

要指定包含為建立物件而呼叫的靜態工廠方法的實際類,
在容器呼叫類上的靜態工廠方法來建立bean的情況下就不太常見了。
從靜態工廠方法呼叫返回的物件型別可以是相同的類,也可以完全是另一個類。
內部類名稱
如果希望為靜態巢狀類配置bean定義,則必須使用巢狀類的二進位制名稱。

例如,如果你在com中有一個叫做什麼的類。
這個東西類有一個名為OtherThing的靜態巢狀類,
bean定義上的class屬性的值將是com.example.SomeThing$OtherThing。

請注意名稱中使用了$字元來分隔巢狀的類名和外部類名。

<bean id="innerClass1" class="org.springframework.example.config.MyBean$InnerClass"/>

用建構函式例項化

當透過構造方法建立一個bean時,所有普通類都可以被Spring使用並與之相容。也就是說,正在開發的類不需要實現任何特定的介面或以特定的方式進行編碼。只需指定bean類就足夠了。但是,根據您用於該特定bean的IoC的型別,您可能需要一個預設(空)建構函式。

Spring IoC容器幾乎可以管理您要管理的任何類。它不僅限於管理真正的JavaBean。大多數Spring使用者更喜歡實際的JavaBean,它僅具有預設(無引數)建構函式,並具有根據容器中的屬性建模的適當的setter和getter。您還可以在容器中具有更多奇特的非bean樣式類。例如,如果您需要使用絕對不符合JavaBean規範的舊式連線池,則Spring也可以對其進行管理。

使用基於XML的配置後設資料,您可以如下指定bean類:

<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

有關用於向建構函式提供引數(如果需要)並在構造物件之後設定物件例項屬性的機制的詳細資訊,請參見 注入依賴項。

用靜態工廠方法例項化

在定義使用靜態工廠方法建立的bean時,請使用class屬性指定包含static工廠方法的類,並使用命名factory-method為屬性的屬性來指定工廠方法本身的名稱。您應該能夠呼叫此方法(使用可選引數,如稍後所述)並返回一個活動物件,該物件隨後將被視為已透過建構函式建立。這種bean定義的一種用法是static用舊程式碼呼叫工廠。

以下bean定義指定透過呼叫工廠方法來建立bean。該定義不指定返回物件的型別(類),而僅指定包含工廠方法的類。在此示例中,該createInstance() 方法必須是靜態方法。以下示例顯示如何指定工廠方法:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
使用例項工廠方法例項化

類似於透過靜態工廠方法進行例項化,使用例項工廠方法進行例項化會從容器中呼叫現有bean的非靜態方法來建立新bean。要使用此機制,請將class屬性留空,並在factory-bean屬性中指定當前(或父容器或祖先容器)中包含要建立物件的例項方法的bean的名稱。使用factory-method屬性設定工廠方法本身的名稱。以下示例顯示瞭如何配置此類Bean:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一個工廠類也可以包含一個以上的工廠方法,如以下示例所示:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

這種方法表明,工廠Bean本身可以透過依賴項注入(DI)進行管理和配置。詳細資訊,請參見依賴性和配置。

在Spring文件中,“ factory bean”是指在Spring容器中配置並透過例項或 靜態工廠方法建立物件的bean 。相反, FactoryBean(注意大寫)是指特定於Spring的 FactoryBean 實現類。

確定Bean的執行時型別

確定特定bean的執行時型別並非易事。Bean後設資料定義中的指定類只是初始類引用,可能與宣告的工廠方法結合使用,或者是FactoryBean可能導致Bean的執行時型別不同的類,或者在例項的情況下根本不設定-級別工廠方法(透過指定factory-bean名稱解析)。另外,AOP代理可以使用基於介面的代理包裝bean例項,而目標Bean的實際型別(僅是其實現的介面)的暴露程度有限。

找出特定bean的實際執行時型別的推薦方法是BeanFactory.getType呼叫指定的bean名稱。這考慮了上述所有情況,並返回了BeanFactory.getBean要針對相同bean名稱返回的物件的型別。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/506/viewspace-2826227/,如需轉載,請註明出處,否則將追究法律責任。

相關文章