Spring的核心技術(三)---Bean簡介
Bean簡介
Spring的IoC容器管理著很多個Bean,這些Bean是通過提供給容器的配置元資料來建立的,例如XML樣式的<bean/>定義。
1. 在容器的Bean定義表示為BeanDefinition物件,它包含了以下元資料:
2. 限定包的類名:通常是實際的定義Bean的實現類;
3. Bean的行為配置元素,定義了容器中的Bean應該如何工作(如範圍、生命週期的回撥等等);
4. Bean工作是所要引用的其他的Bean,這些引用也叫做合作者或依賴;
5. 要設定給新建立物件的其他配置設定,例如,管理連線池Bean中使用的連線的數量,或連線池的大小限定。
這些元資料會轉換成一組構成每個Bean定義的屬性。
Bean的定義屬性
ApplicationContext除了包含如何建立指定的Bean的定義資訊之外,它還允許登記由使用者在容器外部建立的既存的物件。通過訪問由getBeanFactory()方法返回的ApplicationContext的BeanFactory實現的DefaultListableBeanFactory物件來完成物件的註冊,DefaultListableBeanFactory物件通過registerSingleton()和registerBeanDefinition()方法來支援Bean的登記功能。但是,通常應用程式只跟通過元資料的Bean定義所定義的Bean一起工作。
提示:Bean的元資料和手動提供的單例例項需要儘可能早的註冊,以便在自動裝配和執行其他的內部操作期間,容器能夠正確的推匯出相關的Bean。雖然在某種程度上支援重寫既存的元資料和單例的例項,但是官方不支援在執行時註冊新的Bean,這樣可能導致Bean容器內的併發訪問異常或/和狀態的不一致。
Bean的命名
每個Bean都有一個或多個識別符號,這些識別符號必須是主機相應的Bean容器內部唯一的。一個Bean通常只有一個識別符號,但是如果需要多個,額外的那些都被認為是別名。
在基於XML的配置元資料中,使用id和/或name屬性來指定Bean的識別符號。id屬性只允許明確的指定一個,通常名稱是字元數字混合的(如”myBean”,”fooService”等),而且還可以包含特殊字元。如果想要給Bean引入其他的別名,可以在name屬性中使用逗號(”,”)、分號(”;”)或空格(” “)來分割名稱。由於歷史原因,在Spring3.1之前的版本張,id屬性被定義為xsd:ID型別,它限制了可能的字元。版本3.1之後,它被定義為一個xsd:string型別。需要注意的是,雖然不再有XML解析器來處理,但容器依然強制要求Bean的id的唯一性。
給一個Bean提供名稱或id不再是必須的,如果沒有明確的提供名稱或id,容器會給相應的Bean提供一個唯一的名稱。但是,如果想要通過名稱來引用這個Bean(如使用ref元素或Service Locator樣式來查詢),就必須提供一個名稱。起因不會給所使用的內部Bean和自助裝配器提供一個名稱。
Bean的命名規範
在給Bean命名時,使用Java標準的給欄位命名的規範。也就是說,Bean的名稱使用小寫字母開頭,採用駝峰式的命名方式。例如”accountManager”、”accountService”、”userDao”、”loginController”等。
命名的Bean會讓配置更加容易閱讀和理解,當通過名稱來使用一組相關的Bean的時候,使用Spring的AOP會有更大的幫助。
在Bean的定義的外部給一個Bean定義別名
在一個Bean自己的定義裡,可以使用組合的方式給這個Bean提供多個名稱,這些名稱被放在name屬性裡。這些名稱等價於相同Bean的別名,這對某些場景是有用的,如允許應用程式中的每個元件通過使用元件自己所指定的Bean的名稱來應用共通的依賴。
但是Bean實際定義的所有的別名不總是夠用的,某些時候期望引入一個在其他地方定義的別名。在一些大型系統中,配置通常被分割到每個子系統中,每個子系統都有它自己的物件定義集合,在基於XML的配置元資料中,可以使用<alias/>元素來實現這個目的。
<alias name="fromName" alias="toName"/>
在這個案例中,同一個容器中的一個Bean被命名為fromName,使用了這個別名定義之後,也可以應用toName。
例如,子系統A的配置元資料可以通過名稱subsystemA-dataSource來引用一個數據源。子系統B可以通過名稱subsystemB-dataSource來引用一個數據源。當使用這兩個子系統的主應用程式在組裝時,主應用程式通過名稱myApp-dataSource來引用資料來源。這三個名稱都指向由下列別名定義的統一個物件。
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
現在,每個元件和主應用程式都能夠通過一個唯一的名稱指向同一個資料來源,這就保證了跟其他的定義不發生衝突,但是它們依然指向了相同的Bean。
基於Java程式碼的配置
如果使用基於Java程式碼的配置,@Bean註解可以用來提供別名,詳細請看“使用@Bean註解”。
Bean的例項化
一個Bean的定義本質上是建立一個或多個物件的配方。在被要求例項化時,容器使用Bean的名稱來查詢這個配方,並且使用定義該Bean的配置元資料來建立(或獲取)一個實際的物件。
如果使用基於XML的配置元資料,在<bean/>元素的class屬性中指定要例項化的物件的型別(或類)。這個class屬性對應了BeanDefinition物件例項上的Class屬性,通常是強制要求的(例外的情況,請看“使用例項工廠方法來例項化”和“Bean定義的繼承”)。有以下兩種使用Class屬性的方法:
A. 通常情況下,容器通過反射的方式呼叫相關Bean的構造器來直接建立指定的Bean,這有點像Java程式碼中使用的new操作符。
B. 呼叫指定的包含static工廠方法的類來建立物件,容器呼叫類上的static工廠方法來建立Bean的情況非常少見。從呼叫的static工廠方法返回的的物件型別可以是相同的類或其他的類。
內部類的命名
如果要給一個靜態的內部類配置一個Bean定義,必須使用內部類的二進位制名稱。
例如,在com.example包張有一個Foo類,這個類有一個內部類Bar,在Bean定義中的”class”屬性的值應該是com.example.Foo$Bar。
注意,在上面的命名中使用了$符把內部類的名稱跟外部類的名稱分開。
使用構造器來例項化
通過構造器的方法來建立Bean適用於所有的普通的類,並且與Spring相容。也就是說,開發的類不需要實現任何特殊的介面或特定格式的編碼。只需簡單的指定Bean的類就足夠了。但是對於有些特殊的Bean會依賴於你所使用的IoC的型別,可能需要一個預設的構造器。
實際上Spring的IoC容器可以管理所需要的任意型別的類。它並不限定於管理純粹的JavaBean。大多數的Spring使用者都偏好容器中包含只有預設構造器(沒有引數)和相應的get、set方法的JavaBean,當然也可以在容器中也可以包含很多非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的名稱,這個Bean中包含了要呼叫的用來建立物件的例項方法。factory-method屬性用於指定要呼叫的工廠方法的名稱。
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
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();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
這種方法展示了工廠Bean自己能夠通過依賴注入(DI)被管理和配置,詳細請看“依賴和配置的詳細資訊”。
提示:在Spring文件中,factorybean指在Spring容器中配置的一個Bean,它會通過例項或靜態工廠方法來建立物件。而FactoryBean(注意大寫)特指Spring的FactoryBean介面。