這章包括了Spring框架對於IoC規則的實現。Ioc也同DI(依賴注入)。而對象是經過構造函數,工廠方法,或者一些Set方法來定義對象之間的依賴的。容器在建立這些Bean對象的時候同時就會注入這些依賴。這個過程是根本上的反轉了,再也不由Bean自己來控制實例化和定位依賴,而是經過服務定位來控制這個過程,也是IoC(控制反轉)的由來。javascript
org.springframework.beans
和org.springframework.context
包是Spring框架IoC容器的基礎。BeanFactory
接口提供了一種先進的配置機制可以管理任何類型的對象。ApplicationContext
是BeanFactory
的子接口。它增長了一些跟Spring AOP特性更爲簡單的集成,包括信息資源處理(國際化使用),事件發表,以及應用層特別上下文的使用。html
簡而言之,BeanFactory
提供了框架配置和基本的功能,而ApplicationContext
提供了更多企業級特性。ApplicationContext
是BeanFactory
的超集,並且在這章Spring IoC容器中惟一使用的。想要更多的瞭解BeanFactory
的話,請參考6.16。java
在Spring中,那些在你應用中,由Spring IoC容器管理的骨幹對象,都叫作Bean。Bean就是一個由Spring IoC容器實例化,裝載,以及管理的對象。Bean也是你應用中的對象。Bean以及Bean的那些依賴對象,都是經過容器使用的元數據反射成的。web
接口org.springframework.context.ApplicationContext
表示Spring IoC容器同時負責實例化,配置,以及裝載前面說起的Bean對象。容器經過讀取配置元數據來知道那些對象須要實例化,配置以及裝載。配置元數據能夠寫到XML中,Java註解中,或者Java代碼中。spring
幾種不一樣的由Spring針對ApplicationContext接口的實現都是能夠直接使用的。在單機環境中,使用ClassPathXmlApplicationContext
或者FileSystemXmlApplicationContext
也是很是常見的。儘管XML是傳統的定義元數據的格式,你也能夠經過Java註解或者代碼來提供額外的元數據。markdown
在大多數應用場景中,用戶代碼不須要實例化Spring IoC容器。好比,在Web應用場景下,只須要在web.xml中添加少數幾行代碼就能夠由Web容器來建立Spring IoC容器。架構
下面的圖是一個high-level的Spring工做圖。你的應用的類以及配置當中的元數據只有在ApplicationContext建立了,初始化好,你纔有一個徹底配置好的,可執行的系統或應用。併發
圖6.1 Spring IoC 容器app
如前面的圖所表現的,Spring IoC容器會使用配置元數據。這個數據也表示了你但願Spring容器在應用中如何來實例化,配置,以及裝載對象。框架
配置元數據傳統的提供方式是使用簡單直觀的XML格式,固然也是本章所用來表達Spring IoC容器的一些關鍵的概念和特性的格式。
基於XML格式的元數據配置不是惟一的配置元數據的方式。Spring IoC容器自己和使用哪種元數據來寫入配置是徹底解耦的。目前不少開發者也選擇使用基於Java的方式來配置Spring應用。
關於使用其餘不一樣形式的元數據,能夠參考
Spring 配置包括至少一種Bean的定義方式。基於XML配置元數據都是經過配置< bean/>這樣的標籤,在最高級別的< beans/>標籤之下。也能夠經過Java 配置使用 @Bean註解的方法到使用@Configuration註解的類上面。
這些Bean定義所關聯的實際的對象構成了你的應用。一般,你能夠定義服務層對象,數據接入層對象(Dao),表現層對象好比Struts裏面的Action
實例,基礎構成對象好比Hibernate的SessionFactories
,JMSQueues
等等。一般不配置細粒度的域對象的容器,由於它一般是DAOs的責任和業務邏輯建立和加載域對象。然而,你可使用Spring和AspectJ集成來配置在IoC容器控制以外的對象。能夠參考文章 Using AspectJ to dependency-inject domain objects with Spring.
。
下面的例子展現了基於XML的配置元數據的基本結構
<?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 id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
id
屬性是一個字符串,是用來區分獨立的Bean定義的。class
屬性定義了Bean使用的類型,用的是全名。id的值用來讓Bean對象之間相互引用。
實例化Spring IoC容器很直接。ApplicationContext
的構造函數能夠經過一些資源地址的字符串來讓容器從中加載配置元數據。這些文件能夠來自本地文件系統,或者Java的CLASSPATH等。
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
以下的例子展現了一個服務層對象(services.xml)的配置文件。
<?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">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
以下的例子展現了一個數據接入層對象(daos.xml)文件
<?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 id="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
在以前的例子中,服務層對象包括了類PetStoreServiceImpl
,而且兩個數據接入對象類型分別是JpaAccountDao
以及JpaItemDao
(基於JPA O/R mapping 標準)。property name
元素指的是Bean屬性的名字,ref
指的是另外一個定義的bean。這種id和ref元素的關聯,表示了不一樣對象之間的依賴關係。
有時將bean定義到多個XML文件更清晰一些。一般來講,在開發者的架構中一個單獨的XML配置文件表明一個單獨的邏輯層,或者單獨的模塊。
開發者可使用應用上下文的構造函數來加載這些包含bean的XML。這個構造函數可使用多個資源路徑,好比以前一節中展現的那樣。或者可使用一個或者多個< import/>標籤來加載bean定義。以下:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
在上述例子中,外部的Bean定義經過3個文件:services.xml,messageSource.xml以及themeSource.xml來加載。全部路徑都是相對於當前文件的所在的路徑。因此services.xml必須和當前文件在同一個路徑或classpath路徑。而messageSource.xml和themeSource.xml必須定義在resources路徑下。如上所述,第一個斜線是被忽視掉的,考慮到這些路徑都是相對的,最好不要使用第一個下線。這些文件的內容是被引用的,包括最高級的< beans/>元素,因此這些文件針對Spring Bean的XML定義必須有效。
極可能,經過使用相對路徑」../」來得到引用的文件,可是並不推薦這樣作。這樣作會建立一個針對當前應用的外部依賴。尤爲是這個路徑中包含「classpath」,注入這樣的URL(好比,「classpath:../services.xml」),這樣會引用一個運行時的classpath根目錄,而後在查找其父目錄。Classpath配置的改變可能會變成一個徹底不同的目錄。
固然,你也可使用一些完整的路徑而不使用相對路徑,好比像「file:C:/config/service.xml」或者「classpath:/config/services.xml」。然而,必定要注意這樣作你是在耦合你的應用到你本地的絕對路徑上。一般,更好的方式是使用引用來針對這些絕對路徑,好比「${…}」這類佔位符,JVM系統是能夠在運行時解析的。
ApplicationContext
是一個負責註冊不一樣Bean的工廠接口。能夠經過T getBean(String name, Class<T> requiredType)
方法獲取Bean的實例。
獲取的代碼以下:
// create and configure beans
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
開發者可使用getBean()
來得到Bean對象。ApplicationContext
接口有幾個方法來獲取Bean實例,可是開發者的代碼中可能用不到這些方法。事實上,開發者的應用不應調用getBean()
方法,且不依賴於Spring的API。好比,Spring與Web框架的集成,爲多種框架的控制層等提供了依賴注入。
Spring IoC容器管理了不少的Bean對象。這些Bean對象都是根據容器的配置元數據所建立的,好比基於XML的<bean/>
定義。
在容器裏面,Bean的定義都被表現爲BeanDefinition
對象,包含如下元數據:
除了使用實現定義的元數據來建立Bean,ApplicationContext
的實現也容許開發者將已存在的容器外的對象註冊爲Bean對象。能夠經過進去ApplicationContext的BeanFactory中的getBeanFactory()
方法來得到在DefaultListableBeanFactory
中實現的BeanFactory。DefaultListableBeanFactory
經過registerSingleton(..)
以及registerBeanDefinition(..)
支持前面的操做。然而,一般狀況下,應用都只是使用元數據中定義的Bean對象。
Bean的元數據以及手工支持的單例的最好儘早註冊到Spring容器中,防止容器在裝載這些Bean的過程當中發生錯誤。然而,覆蓋掉已存在的元數據和存在的單例Bean也是支持的,可是在運行時註冊Bean有在官方上並不支持,並且由於Bean狀態的不一致致使併發異常。
每個Bean都有不止一個區分符。這些區分符必須在這個容器中惟一。一般,一個Bean只有一個區分符,可是若是多餘一個,那麼額外的區分符也做爲這個Bean的別名。
在基於XML配置的元數據中,你可使用id
或者name
屬性來做爲Bean的區分符。id
屬性容許你特指惟一的一個id。方便起見,這個名字都是有字符跟數字的(‘myBean’, ‘fooService’等),也能夠包含特殊的字符。若是你也經過其餘別名來使用Bean,開發者也能夠給Bean使用name
屬性,以,
,;
或者空格來區分。因爲歷史的緣由,在Spring 3.1以前,id
屬性是被定義成一種xsd:ID
類型的。在3.1中,id的類型還被定義成xsd:string
類型。
開發者也能夠不給Bean定義id或者name。若是Bean沒有名字或者id的話,容器會幫助Bean生成一個獨特的名字。可是若是你想使用ref
這樣的元素來定位到Bean的話,你仍是須要添加一個名字的。不使用名字主要是爲了使用內在的Bean以及聯合裝載。
Bean的命名習慣
通常習慣就是根據Java的field變量的方式來命名。以小寫開始的駝峯命名。好比accountManager
,userDao
,loginController
等。
一致的命名方式可讓開發者配置更加簡單易懂,並且若是你使用Spring AOP的話,這樣作也頗有益處。
在Bean定義以外增長別名
在Bean定義自己,開發者經過使用id屬性以及name屬性能夠爲Bean定義多個名字。這些名字也一樣能指向相同的Bean對象,在一些場景下是很實用的。好比容許組件引用多個依賴的話,經過名字會更有效。
然而,在Bean定義的時候來特指別名有的時候是不夠的。有的時候引用別名來定義在其餘的地方可以更清晰。這也是大系統的一些常見場景,根據不一樣的子系統來區分配置信息,每一個子系統都有本身的定義。在基於XML的配置元數據中,可使用<alias/>
元素來作到。
<alias name="fromName" alias="toName"/>
這種狀況下,在仙童的容器中,全部name是fromName
的Bean,也能經過alias定義來經過toName
來引用。
舉例來講,子系統A的元數據可能會經過一個subsystemA-dataSource
來引用其數據源。而子系統B的配置元數據可能經過subsystemB-dataSource
來引用數據源。當組合成一個應用時,會同時使用這兩個子系統經過myApp-dataSource
來引用數據源。若是但願經過3個名字來指向一個對象,你能夠經過應用的配置元數據配置以下定義。
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
如今每一個組件和主應用程序能夠經過名稱引用數據源是獨一無二的,保證不與其餘任何衝突定義(有效地建立一個名稱空間),然而他們引用同一個bean。
Bean定義的自己其實也是建立Bean對象的菜譜,容器經過這個定義來的元數據來將建立實際的Bean對象。
若是開發者使用的是基於XML的配置元數據,開發者能夠經過特指Bean的class
字段來肯定Bean被實例化成指定的對象。class
屬性一般來講,在Bean定義中是必須的。開發者能夠經過以下方式使用Class
屬性:
new
操做符。內部類。若是開發者想經過配置一個Bean爲靜態內部類,開發者須要指定二進制的嵌套類的類名。
舉例來講,好比有一個類名爲Foo
在包com.example
包之中,並且這個Foo
類其中有一個靜態的嵌套類叫作Bar
,那麼若是想使用Bar
來做爲Bean的話,它的class
屬性須要爲
com.example.Foo$Bar
須要注意的是,$
符號就是用來區外部類和內部類的。
經過構造函數實例化
當開發者經過構造函數來建立Bean對象的時候,全部的普通的類都可以和Spring協同工做。也就是說,通常的做爲Bean的類是不須要實現一些特殊的接口的。僅僅指定Bean的類就足夠了。然而,根據你使用IoC容器的不一樣,開發者可能須要配置默認(無參)構造函數。
Spring IoC容器能夠幫你管理任何你想要管理的類。並不只限於Bean對象。大多數的Spring開發者更多在容器中使用Bean對象配合getter,setter方法以及無參的構造函數。固然,開發者也能夠在容器中管理一些非Bean樣式的對象。好比說,一個不被引用的鏈接池,Spring仍然能夠管理它。
使用基於XML的元數據配置方式,Bean的配置能夠以下:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
經過靜態工廠方法實例化
當經過靜態工廠方法來定義Bean對象的時候,開發者可使用class
屬性來指定包含工廠方法的類,經過factory-method
來指定生成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。同時將這個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自己也是能夠經過依賴注入配置的。
在Spring文檔中,工廠Bean指的是在Spring容器中配置的專門經過實例方法或者靜態方法來建立Bean的一個Bean。相對而言,
FactoryBean
指的是Spring一種特指的FactoryBean
.