Spring是一個輕量級的Java開發(fā)框架,其提供的兩大基礎(chǔ)功能為IoC和AOP,其中IoC為依賴反轉(zhuǎn)(Inversion of Control)。IOC容器的基本理念就是“為別人服務(wù)”,那為別人服務(wù)什么呢?其中最重要就是業(yè)務(wù)對象的構(gòu)建管理和業(yè)務(wù)對象之間的依賴綁定。
- 業(yè)務(wù)對象的構(gòu)建管理:業(yè)務(wù)場景中,無需關(guān)心業(yè)務(wù)對象如何創(chuàng)建、如何管理,在需要時只需獲取即可。業(yè)務(wù)對象的構(gòu)建管理交給IoC容器,避免這部分代碼對業(yè)務(wù)邏輯的侵染。
- 業(yè)務(wù)對象之間的依賴綁定:IoC容器需要先了解業(yè)務(wù)對象之間的依賴關(guān)系,這樣依據(jù)之前業(yè)務(wù)對象的構(gòu)建管理就可以對外提供IoC服務(wù),保證每個業(yè)務(wù)對象在使用時處于就緒狀態(tài)。
IoC容器管理業(yè)務(wù)對象,首先需要知道業(yè)務(wù)對象之間的依賴關(guān)系,以下有幾種方式告訴IoC容器其管理的對象之間的綁定關(guān)系:
- 可以通過簡單的文本方式記錄被注入對象和其依賴對象的對應(yīng)關(guān)系。
- 使用描述性更強的XML文件格式記錄對象之間的對應(yīng)關(guān)系。
- 還可以通過編寫代碼的方式(調(diào)用IoC容器提供的對應(yīng)API)設(shè)置對象之間的關(guān)系。
- ...
注意:不管是什么方式來告知IoC容器對象之間的綁定關(guān)系,最終都是通過編碼方式(調(diào)用IOC提供的API)來將這些信息"寫入"到IoC容器中的。
2種基本的容器類型
Spring的IoC容器提供兩種基本的容器類型:BeanFactory和ApplicationContext。
- BeanFactory:基礎(chǔ)類型IoC容器,提供基本的容器服務(wù),如果沒有特殊指定,采用延遲初始化策略,也就是當(dāng)客戶端需要容器中某個對象時,才對該受管理的對象初始化及其依賴注入操作。所以,相對來說,BeanFactory容器啟動較快,所需資源有限,對于資源有限,并且功能要求不嚴(yán)格的場景,使用BeanFactory容器是比較合適的。
- ApplicationContext:ApplicationContext是在BeanFactory基礎(chǔ)之上構(gòu)建的,是一個比較高級的容器,除了擁有BeanFactory的全部功能外,也提供其他高級特性,比如事件發(fā)布、國際化信息支持等。ApplicationContext所管理的對象,默認ApplicationContext啟動之后全部初始化并綁定完成,所以其啟動較慢,占用資源較多。在系統(tǒng)資源充足,并需要提供較多功能的使用場景,ApplicationContext是一個不錯的選擇。
依賴注入的3種方式
- 構(gòu)造方法注入:調(diào)用被注入對象的構(gòu)造方然注入,優(yōu)點是對象在構(gòu)造完成就進入了就緒狀態(tài)。
- 屬性注入:調(diào)用被注入對象的setter/getter方法。
- 接口注入:現(xiàn)在不提倡的一種方式,因為它強制被注入對象實現(xiàn)不必要的接口,帶有侵入性。
spring的bean配置(XML配置方式)
屬性注入即通過 setter 方法注入Bean 的屬性值或依賴的對象,使用 <property> 元素, 使用 name 屬性指定 Bean 的屬性名稱,value 屬性或 <value> 子節(jié)點指定屬性值,屬性注入是實際應(yīng)用中最常用的注入方式。屬性注入Bean類須有一個默認的構(gòu)造方法。
<!-- Hello類中有一個String類型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg" value="luoxn28"/>
</bean>
通過構(gòu)造方法注入Bean 的屬性值或依賴的對象,它保證了 Bean 實例在實例化后就可以使用,構(gòu)造器注入在 <constructor-arg> 元素里聲明屬性。
<bean id="msg" class="java.lang.String">
<constructor-arg value="string"/>
</bean>
<!-- 按照索引匹配入?yún)?-->
<bean id="car" class="com.luoxn28.Car">
<constructor-arg value="比亞迪" index="0"/>
<constructor-arg value="中國制造" index="1"/>
<constructor-arg value="200000" index="2"/>
</bean>
<!-- 按照類型匹配入?yún)?-->
<bean id="car2" class="com.luoxn28.Car">
<constructor-arg value="比亞迪" type="java.lang.String">
<constructor-arg value="中國制造" type="java.lang.String"/>
<constructor-arg value="200000" type="double"/>
</bean>
多個Bean依賴配置,需在Bean配置中指定對Bean的引用。既可以使用"ref"屬性配置,也可以直接在屬性里包含Bean的配置,即內(nèi)部Bean。內(nèi)部Bean只能在當(dāng)前定義處使用,貌似其他地方也沒法使用哈 :)
<bean id="msg" class="java.lang.String">
<constructor-arg value="luoxn28"/>
</bean>
<!-- Hello類中有一個String類型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg" ref="msg"/>
</bean>
<!-- Hello類中有一個String類型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg">
<bean class="java.lang.String">
<constructor-arg value="luoxn28"/>
</bean>
</property>
</bean>
如果Bean屬性是集合/容器類型,則通過<list>/<set>/<map>來配置。
<!-- CollectionClass類有3個屬性,List<String> list、Set<String> set、Map<String, String> map-->
<bean id="collectionClass" class="com.luoxn28.CollectionClass">
<property name="list">
<list>
<value>luoxn28</value>
<value>luoxn29</value>
<value>luoxn30</value>
</list>
</property>
<property name="set">
<set>
<value>luoxn28</value>
<value>luoxn29</value>
<value>luoxn30</value>
</set>
</property>
<property name="map">
<map>
<entry key="str1" value="luoxn28"/>
<entry key="str2"><value>luoxn29</value></entry>
<entry key="str3"><value>luoxn30</value></entry>
</map>
</property>
</bean>
Bean的作用域
Spring最初提供ean的兩種scope類型:singleton和prototype,在發(fā)布2.0之后,新增了request、session和global session類型,不過這3種新增的只能用在Web應(yīng)用中。可以通過bean屬性scope來指定bean的scope類型,如果是singleton類型的話,在用戶獲取該bean之后,容器還是會接管該bean的生命周期;如果是prototype的話,在用戶獲取該bean之后,容器就不接管該bean了,也就是容器每次會創(chuàng)建一個新的bean對象返回給用戶。
<!-- Hello對象每次獲取都會新建 -->
<bean id="hello" class="com.luoxn28.Hello" scope="prototype">
<property name="msg" value="luoxn28"/>
</bean>
通過靜態(tài)方法創(chuàng)建bean
調(diào)用靜態(tài)工廠方法創(chuàng)建 Bean是將對象創(chuàng)建的過程封裝到靜態(tài)方法中,當(dāng)用戶需要對象時,只需要簡單地調(diào)用靜態(tài)方法,而不同關(guān)心創(chuàng)建對象的細節(jié)。要聲明通過靜態(tài)方法創(chuàng)建的 Bean, 需要在 Bean 的 class 屬性里指定擁有該工廠的方法的類, 同時在 factory-method 屬性里指定工廠方法的名稱. 最后, 使用 <constrctor-arg> 元素為該方法傳遞方法參數(shù)。
public static Hello createHello() {
return new Hello();
}
<bean id="hello" class="com.luoxn28.Hello" factory-method="createHello">
</bean>
BeanFactory
Spring 中有兩種類型的 Bean, 一種是普通Bean,另一種是工廠Bean,即FactoryBean。工廠 Bean 跟普通Bean不同,,其返回的對象不是指定類的一個實例,其返回的是該工廠 Bean 的 getObject 方法所返回的對象
FactoryBean接口源碼如下所示:
public interface FactoryBean<T> {
// 返回的實例
T getObject() throws Exception;
// 返回的類型
Class<?> getObjectType();
// 是否為單例
boolean isSingleton();
}
BeanFactory使用示例:
public class Hello {
private String name;
private int age;
// set/get方法
}
public class HelloBeanFactory implements FactoryBean<Hello> {
@Override
public Hello getObject() throws Exception {
Hello hello = new Hello();
hello.setName("luoxn28");
hello.setAge(23);
return hello;
}
@Override
public Class<?> getObjectType() {
return Hello.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
然后在applicationContext.xml中配置如下,就可以獲取Hello類實例了。
<bean id="helloBean" class="com.luoxn28.hello.HelloBeanFactory">
</bean>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Hello hello = context.getBean("helloBean", Hello.class);
System.out.println(hello);
}
參考資料:
- Spring的IoC容器
- 《Spring揭秘》IOC章節(jié)