Spring是一個(gè)輕量級(jí)的Java開(kāi)發(fā)框架耿导,其提供的兩大基礎(chǔ)功能為IoC和AOP,其中IoC為依賴(lài)反轉(zhuǎn)(Inversion of Control)。IOC容器的基本理念就是“為別人服務(wù)”,那為別人服務(wù)什么呢巩螃?其中最重要就是業(yè)務(wù)對(duì)象的構(gòu)建管理和業(yè)務(wù)對(duì)象之間的依賴(lài)綁定。
- 業(yè)務(wù)對(duì)象的構(gòu)建管理:業(yè)務(wù)場(chǎng)景中匕争,無(wú)需關(guān)心業(yè)務(wù)對(duì)象如何創(chuàng)建、如何管理爷耀,在需要時(shí)只需獲取即可甘桑。業(yè)務(wù)對(duì)象的構(gòu)建管理交給IoC容器,避免這部分代碼對(duì)業(yè)務(wù)邏輯的侵染歹叮。
- 業(yè)務(wù)對(duì)象之間的依賴(lài)綁定:IoC容器需要先了解業(yè)務(wù)對(duì)象之間的依賴(lài)關(guān)系跑杭,這樣依據(jù)之前業(yè)務(wù)對(duì)象的構(gòu)建管理就可以對(duì)外提供IoC服務(wù),保證每個(gè)業(yè)務(wù)對(duì)象在使用時(shí)處于就緒狀態(tài)咆耿。
IoC容器管理業(yè)務(wù)對(duì)象德谅,首先需要知道業(yè)務(wù)對(duì)象之間的依賴(lài)關(guān)系,以下有幾種方式告訴IoC容器其管理的對(duì)象之間的綁定關(guān)系:
- 可以通過(guò)簡(jiǎn)單的文本方式記錄被注入對(duì)象和其依賴(lài)對(duì)象的對(duì)應(yīng)關(guān)系萨螺。
- 使用描述性更強(qiáng)的XML文件格式記錄對(duì)象之間的對(duì)應(yīng)關(guān)系窄做。
- 還可以通過(guò)編寫(xiě)代碼的方式(調(diào)用IoC容器提供的對(duì)應(yīng)API)設(shè)置對(duì)象之間的關(guān)系。
- ...
注意:不管是什么方式來(lái)告知IoC容器對(duì)象之間的綁定關(guān)系慰技,最終都是通過(guò)編碼方式(調(diào)用IOC提供的API)來(lái)將這些信息"寫(xiě)入"到IoC容器中的椭盏。
2種基本的容器類(lèi)型
Spring的IoC容器提供兩種基本的容器類(lèi)型:BeanFactory和ApplicationContext。
- BeanFactory:基礎(chǔ)類(lèi)型IoC容器吻商,提供基本的容器服務(wù)掏颊,如果沒(méi)有特殊指定,采用延遲初始化策略艾帐,也就是當(dāng)客戶(hù)端需要容器中某個(gè)對(duì)象時(shí)乌叶,才對(duì)該受管理的對(duì)象初始化及其依賴(lài)注入操作。所以柒爸,相對(duì)來(lái)說(shuō)准浴,BeanFactory容器啟動(dòng)較快,所需資源有限揍鸟,對(duì)于資源有限兄裂,并且功能要求不嚴(yán)格的場(chǎng)景句旱,使用BeanFactory容器是比較合適的。
- ApplicationContext:ApplicationContext是在BeanFactory基礎(chǔ)之上構(gòu)建的晰奖,是一個(gè)比較高級(jí)的容器谈撒,除了擁有BeanFactory的全部功能外,也提供其他高級(jí)特性匾南,比如事件發(fā)布啃匿、國(guó)際化信息支持等。ApplicationContext所管理的對(duì)象蛆楞,默認(rèn)ApplicationContext啟動(dòng)之后全部初始化并綁定完成溯乒,所以其啟動(dòng)較慢,占用資源較多豹爹。在系統(tǒng)資源充足裆悄,并需要提供較多功能的使用場(chǎng)景,ApplicationContext是一個(gè)不錯(cuò)的選擇臂聋。
依賴(lài)注入的3種方式
- 構(gòu)造方法注入:調(diào)用被注入對(duì)象的構(gòu)造方然注入光稼,優(yōu)點(diǎn)是對(duì)象在構(gòu)造完成就進(jìn)入了就緒狀態(tài)。
- 屬性注入:調(diào)用被注入對(duì)象的setter/getter方法孩等。
- 接口注入:現(xiàn)在不提倡的一種方式艾君,因?yàn)樗鼜?qiáng)制被注入對(duì)象實(shí)現(xiàn)不必要的接口,帶有侵入性肄方。
spring的bean配置(XML配置方式)
屬性注入即通過(guò) setter 方法注入Bean 的屬性值或依賴(lài)的對(duì)象冰垄,使用 <property> 元素, 使用 name 屬性指定 Bean 的屬性名稱(chēng),value 屬性或 <value> 子節(jié)點(diǎn)指定屬性值权她,屬性注入是實(shí)際應(yīng)用中最常用的注入方式虹茶。屬性注入Bean類(lèi)須有一個(gè)默認(rèn)的構(gòu)造方法。
<!-- Hello類(lèi)中有一個(gè)String類(lèi)型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg" value="luoxn28"/>
</bean>
通過(guò)構(gòu)造方法注入Bean 的屬性值或依賴(lài)的對(duì)象隅要,它保證了 Bean 實(shí)例在實(shí)例化后就可以使用写烤,構(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="中國(guó)制造" index="1"/>
<constructor-arg value="200000" index="2"/>
</bean>
<!-- 按照類(lèi)型匹配入?yún)?-->
<bean id="car2" class="com.luoxn28.Car">
<constructor-arg value="比亞迪" type="java.lang.String">
<constructor-arg value="中國(guó)制造" type="java.lang.String"/>
<constructor-arg value="200000" type="double"/>
</bean>
多個(gè)Bean依賴(lài)配置拾徙,需在Bean配置中指定對(duì)Bean的引用洲炊。既可以使用"ref"屬性配置,也可以直接在屬性里包含Bean的配置尼啡,即內(nèi)部Bean暂衡。內(nèi)部Bean只能在當(dāng)前定義處使用,貌似其他地方也沒(méi)法使用哈 :)
<bean id="msg" class="java.lang.String">
<constructor-arg value="luoxn28"/>
</bean>
<!-- Hello類(lèi)中有一個(gè)String類(lèi)型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg" ref="msg"/>
</bean>
<!-- Hello類(lèi)中有一個(gè)String類(lèi)型的msg屬性 -->
<bean id="hello" class="com.luoxn28.Hello">
<property name="msg">
<bean class="java.lang.String">
<constructor-arg value="luoxn28"/>
</bean>
</property>
</bean>
如果Bean屬性是集合/容器類(lèi)型崖瞭,則通過(guò)<list>/<set>/<map>來(lái)配置狂巢。
<!-- CollectionClass類(lèi)有3個(gè)屬性,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類(lèi)型:singleton和prototype藻雌,在發(fā)布2.0之后,新增了request斩个、session和global session類(lèi)型胯杭,不過(guò)這3種新增的只能用在Web應(yīng)用中∈苌叮可以通過(guò)bean屬性scope來(lái)指定bean的scope類(lèi)型做个,如果是singleton類(lèi)型的話(huà),在用戶(hù)獲取該bean之后滚局,容器還是會(huì)接管該bean的生命周期居暖;如果是prototype的話(huà),在用戶(hù)獲取該bean之后藤肢,容器就不接管該bean了太闺,也就是容器每次會(huì)創(chuàng)建一個(gè)新的bean對(duì)象返回給用戶(hù)。
<!-- Hello對(duì)象每次獲取都會(huì)新建 -->
<bean id="hello" class="com.luoxn28.Hello" scope="prototype">
<property name="msg" value="luoxn28"/>
</bean>
通過(guò)靜態(tài)方法創(chuàng)建bean
調(diào)用靜態(tài)工廠(chǎng)方法創(chuàng)建 Bean是將對(duì)象創(chuàng)建的過(guò)程封裝到靜態(tài)方法中嘁圈,當(dāng)用戶(hù)需要對(duì)象時(shí)跟束,只需要簡(jiǎn)單地調(diào)用靜態(tài)方法特铝,而不同關(guān)心創(chuàng)建對(duì)象的細(xì)節(jié)。要聲明通過(guò)靜態(tài)方法創(chuàng)建的 Bean, 需要在 Bean 的 class 屬性里指定擁有該工廠(chǎng)的方法的類(lèi), 同時(shí)在 factory-method 屬性里指定工廠(chǎng)方法的名稱(chēng). 最后, 使用 <constrctor-arg> 元素為該方法傳遞方法參數(shù)台谢。
public static Hello createHello() {
return new Hello();
}
<bean id="hello" class="com.luoxn28.Hello" factory-method="createHello">
</bean>
BeanFactory
Spring 中有兩種類(lèi)型的 Bean愧捕, 一種是普通Bean,另一種是工廠(chǎng)Bean乳愉,即FactoryBean。工廠(chǎng) Bean 跟普通Bean不同,,其返回的對(duì)象不是指定類(lèi)的一個(gè)實(shí)例仗岖,其返回的是該工廠(chǎng) Bean 的 getObject 方法所返回的對(duì)象
FactoryBean接口源碼如下所示:
public interface FactoryBean<T> {
// 返回的實(shí)例
T getObject() throws Exception;
// 返回的類(lèi)型
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類(lèi)實(shí)例了览妖。
<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é)