1、Spring IOC容器和Bean簡介
IOC也稱為依賴注入(DI)。IOC是指: 對象通過構造函數參數、工廠方法的參數或從工廠方法返回后在對象實例上設置的屬性來定義其依賴項, 然后容器在創(chuàng)建Bean時注入那些依賴項。本質上是通過使用類的直接構造或諸如服務器定位之類的控件來控制其依賴項的實例化。
Spring框架中org.spring.framework.context和org.spring.framework.beans軟件包時實現IOC的基礎。BeanFactory接口提供了一種高級配置機制, 能夠Management任何類型的對象, 提供了配置框架和基本功能。ApplicationContext是BeanFactory的子接口, 添加了更多企業(yè)定制的功能。它增加了:
- 與Spring的AOP功能輕松集成。
- 消息資源處理(用于國際化)。
- Event publication。
- 特定于應用程序的上下文。比如用于Web應用程序的
WebApplicationContext。
在Spring中, 構成應用程序并由IOC容器Management的對象稱之為bean。 Bean是由Spring IOC容器初始化、組裝和其他方式Management的對象, 否則他只是一個普通的類。Bean及其之間的依賴關系反映在容器使用的配置元數據中。
2、IOC容器概述
org.spring.framework.context.ApplicationContext接口代表Spring IOC容器, 并負責實例化、配置和組裝Bean。
容器通過讀取配置元數據來獲取有關要實例化、配置和組裝哪些對象那個的指令。 配置元數據以XML、Java Annotation、Java代碼表示。
Spring提供了ApplicationContext的幾種接口實現。通常在獨立應用程序中, 通過創(chuàng)建ClassPathXmlApplicationContext和FileSystemXmlApplicationContext實例來獲取IOC容器。
Spring工作原理的高級視圖如下:

2.1 配置元數據
Spring IOC容器使用一種形式的配置元數據。此配置元數據表示告訴Spring容器如何實例化、配置和組裝應用程序中的對象。傳統(tǒng)中,Spring一般使用XML的格式配置元數據。
Spring提供了3種配置元數據的方式:
- 基于XML的配置方式。
- 基于Annotation-based configuration的配置方式, Spring 2.5 引入了對基于注解的配置元數據的支持。
- 基于Java-based configuration
的配置方式,從 Spring 3.0 開始,Spring JavaConfig 項目提供的許多功能成為了核心 Spring Framework 的一部分。因此,您可以使用 Java 而不是 XML 文件來定義應用程序類外部的 bean。要使用這些新功能,請參見@Configuration,@Bean,@Import和@DependsOn注解。
Spring配置由容器Management的至少一個bean定義組成。基于XML的配置元數據將這些beans配置為頂級<beans>元素內的<bean>元素, 基于Java配置通常在@Configuation類中定義@Bean注解的方法。
這些beans定義對應于組成應用程序的實際對象, 通常定義服務層對象(service)、數據訪問層對象(dao)、表示層對象(Structs action對象)、基礎結構對象等。
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="..."> (1) (2)
<!-- 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>
2.2、實例化容器
提供給ApplicationContext構造函數的位置路徑是資源字符串, 這些資源字符串使容器可以從各種外部資源(例如本地文件系統(tǒng), Java classpath)等加載配置元數據。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
以下示例顯示了服務層配置文件service.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>
構成基于XML的配置元數據
通常,每個單獨的XML配置文件都代表體系結構中的邏輯層或模塊。為了使程序的邏輯更加簡潔獨立, 一般應用程序中會存在多個配置文件,可以在應用程序中使用容器的構造函數從多個XML配置文件中加載bean定義,也可以使用<import/>元素從另一個配置文件中加載bean定義。
如下顯示如何從多個配置文件中引入service.xml的dao.xml的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>
2.3、使用容器
ApplicationContext是高級工廠的接口,該工廠能維護不同bean及其依賴關系的注冊表。通常使用T getBean(String name, Class<T> class)可以檢索Bean的實例。
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
ApplicationContext接口還提供了其他幾種檢索Bean的方法, 但是理想情況下,永遠不要使用它們。實際上基于Spring框架,應該根本不用調用getBean()方法, 因此完全不用依賴于Spring API。
3、Bean概述
Spring IOC容器Management一個或多個bean。這些bean是使用提供給容器的配置元數據創(chuàng)建的。 在容器本身內部,這些bean表示為BeanDefinition對象, 其中包含以下元數據:
- 包限定的類名: 通常, 定義了Bean的實際實現類。
- Bean行為配置元素: 用于聲明Bean在容器中的行為(作用域、生命周期回調)等。
- 依賴項: 引用其他Bean進行工作所需的Bean。
- 要在新創(chuàng)建的配置中設置的其他配置項,例如池的大小或在Management連接池的bean的使用的連接數。
該元數據轉換為構建Bean的每一組屬性, 如下所示:
| 屬性名稱 | 說明 |
|---|---|
| Class | Bean的全限定類名 |
| Name | Bean 的名稱 |
| Scope | Bean的生命周期 |
| Constructor arguments | Bean構造函數注入的參數 |
| Properties | Bean屬性注入的參數 |
| Autowiring mode | 自動裝配模式 |
| LazyInit mode | 延遲初始化模式 |
| Initialization method | 初始化方法回調 |
| Destruction method | 銷毀方法回調 |
| FactoryBean Name | 工廠注入的Bean名稱 |
| FactoryMethod Name | 工廠構建的方法名稱 |
除過包含有關如何創(chuàng)建特定bean的信息的bean定義之外, ApplicationContext實現還允許注冊在容器外部的現有對象。 這是通過getBeanFactory()方法訪問 ApplicationContext 的 BeanFactory來完成的,該方法返回 BeanFactoryDefaultListableBeanFactory的實現。 DefaultListableBeanFactory通過registerSingleton(..)和registerBeanDefinition(..)方法支持此注冊。但是,典型的應用程序只能與通過常規(guī) bean 定義元數據定義的 bean 一起使用。
3.1、命名Bean
每個bean具有一個或多個標識符。這些標識符在承載bean的IOC容器中必須唯一。 一個bean通常只有一個標識符,如果有多個,則可以將多余的視為別名。
在基于XML的配置元數據中, 可以使用id屬性和name屬性, 也可以同時指定這兩個屬性。id屬性可以為bean精確指定一個ID, 如果要為bean指定別名, 可以通過name屬性指定。 bean id的唯一性由容器強制檢查(此前有XML解析器執(zhí)行)。
Bean可以不指定 id和name屬性, 如果不指定,容器會為bean生成一個唯一ID。 如果需要 按照名稱檢索bean, 則必須提供一個名稱。
Bean別名
XML的配置方式可以使用alias標簽來配置別名。
<alias name="fromName" alias="toName"/>
Java-configuation方式使用@Bean方式可以提供別名。
3.2、 實例化Bean
Bean定義實質上創(chuàng)建一個或多個對象的方法。當被詢問時, 容器會查看Bean的配置, 并使用bean定義封裝的配置元數據來創(chuàng)建(或獲取)實際對象。
用構造函數實例化
當使用構造函數創(chuàng)建一個Bean時, 所有普通的類都可以被Spring使用并與之兼容。Spring IOC容器幾乎可以Management任何您要Management的類, 不僅限于JavaBean。它僅需要具備默認的無參構造函數, 并具有通過屬性建模的適當的setter和getter方法即可。
例如, 使用XML配置元數據的時候, 可以通過如下方法指定Bean:
<bean id="exampleBean" class="org.example.Something">
<bean name="exampleBean2" class="org.example.Otherthing">
用靜態(tài)工廠方法實例化
定義使用靜態(tài)工廠方法創(chuàng)建的bean時,需要使用class屬性指定包含靜態(tài)工廠方法的類, 并使用名為factory-method的屬性來指定工廠方法本身的名稱。
例如, XML配置:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
靜態(tài)工廠Java代碼:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
用實例工廠方法實例化
與通過靜態(tài)工廠方法實例化類似, 使用實例工廠方法實例化是容器調用現有的bean的非靜態(tài)方法以創(chuàng)建新bean。 要使用此方式, bean的配置元數據中class屬性需要留空, 并且在factory-bean屬性中指定要創(chuàng)建該實例對象的實例方法的bean名稱。
例如, XML配置:
<!-- 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"/>
對應的Java代碼:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
一個工廠類也可以包含一個以上的工廠方法。
4、Dependencies
典型的企業(yè)應用程序不包含單個對象(或 Spring 術語中的 bean)。即使是最簡單的應用程序,也有一些對象可以協同工作,以呈現最終用戶視為一致的應用程序。下一部分將說明如何從定義多個獨立的 Bean 定義到實現對象協作以實現目標的完全實現的應用程序。
4.1、依賴注入(DI)
依賴注入是一個過程, 通過該過程, 對象只能通過構造函數參數、工廠方法參數或構造實例后在實例上面設置屬性來定義其依賴關系。容器在創(chuàng)建bean時注入依賴項。
DI主要有兩種形式: 基于構造函數的依賴注入和基于Setter的依賴注入。
基于構造函數的依賴注入
基于構造函數的DI是通過容器調用具有多個參數的構造函數來完成的。 調用帶有特定參數的靜態(tài)工廠方法構造Bean幾乎是等效的。
以下示例顯示只能基于構造函數的依賴注入的類:
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
-
構造函數參數解析
構造函數參數的解析通過使用參數的類型進行。如果Bean定義的構造參數含義不存在歧義, 則在實例化Bean時,在Bean中定義的的構造函數參數的順序就是構建Bean時使用的構造函數參數的順序。
假設以下類為例:
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
如果 ThingTwo和ThingThree沒有通過繼承關聯, 且不存在任何歧義,因此可以通過下面這種配置正常工作, 無需在<conructor-arg>元素中指參數的索引或者類型。
<beans>
<bean id="thingOne" class="x.y.ThingOne">
<constructor-arg ref="thingTwo"/>
<constructor-arg ref="thingThree"/>
</bean>
<bean id="thingTwo" class="x.y.ThingTwo"/>
<bean id="thingThree" class="x.y.ThingThree"/>
</beans>
但是, 當使用簡單類型時,Spring無法確定值的類型,因此在沒有額外的幫助的情況下無法按照參數類型進行匹配。比如:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
-
構造函數參數類型匹配
上述類型, 可以使用type屬性顯示指定構造函數參數的類型,則容器可以使用簡單的類型匹配。例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
-
構造函數參數索引
可以使用index屬性來顯示指定構造參數的索引,如下例所示:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
它不僅可以解決多個簡單值的歧義問題, 還可以解決多個參數類型相同的問題。
-
構造函數參數名稱
可以使用構造函數參數的名稱來消除歧義, 如下:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
使用以上方式, 在Spring中需要啟用調試狀態(tài)的情況下編譯代碼, 以便Spring可以從構造函數中查找參數名稱?;蛘呤褂?br> @ConstructorProperties JDK注解 顯式命名構造函數參數。
基于Setter的依賴注入
基于Setter的DI實在調用無參的構造函數或靜態(tài)工廠方法以實例化對應的Bean, 然后在Bean上調用setter方法來完成的。
下面顯示一個只能基于Setter來進行依賴注入的類:
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
ApplicationContext支持其 Management 的 bean 的基于構造函數和基于 setter 的 DI。在已經通過構造函數方法注入了某些依賴項之后,它還支持基于 setter 的 DI。