詳解spring的核心機制依賴注入
對于一般的java項目,他們都或多或少有一種依賴型的關(guān)系,也就是由一些互相協(xié)作的對象構(gòu)成的。spring把這種互相協(xié)作的關(guān)系稱為依賴關(guān)系。如a組件調(diào)用b組件的方法,可稱a組件依賴于b組件,依賴注入讓spring的bean以配置文件組織在一起,而不是以硬編碼的方式耦合在一起
一、理解依賴注入
依賴注入(dependency injection) = 控制反轉(zhuǎn)(inversion ofcontrol,ioc):當(dāng)某個java實例(調(diào)用者)需另一個java實例(被調(diào)用者)時,在依賴注入模式下,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此稱為 控制反轉(zhuǎn) ;創(chuàng)建被調(diào)用者實例的工作通常由spring容器來完成,然后注入調(diào)用者,因此也稱為 依賴注入
依賴注入:程序運行過程中,如需另一個對象協(xié)作(調(diào)用它的方法、訪問他的屬性)時,無須在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部容器的注入。spring的依賴注入對調(diào)用者和被調(diào)用者幾乎無任何要求,完全支持對pojo間依賴關(guān)系的管理
依賴注入
設(shè)值注入:ioc容器使用屬性的setter方法來注入被依賴的實例
構(gòu)造注入:ioc容器使用構(gòu)造器來注入被依賴的實例
理解依賴注入:
一個人(java實例,調(diào)用者)需要一把斧子(java實例,被調(diào)用者)
在原始社會里,幾乎沒有社會分工;需要斧子的人(調(diào)用者)只能自己去磨一把斧子(被調(diào)用者);對應(yīng)情形為:java程序里的調(diào)用者自己創(chuàng)建被調(diào)用者,通常采用new關(guān)鍵字調(diào)用構(gòu)造器創(chuàng)建一個被調(diào)用者
進入工業(yè)社會,工廠出現(xiàn)了,斧子不再由普通人完成,而在工廠里被生產(chǎn)出來,此時需要斧子的人(調(diào)用者)找到工廠,購買斧子,無須關(guān)心斧子的制造過程;對應(yīng)簡單工廠設(shè)計模式,調(diào)用者只需定位工廠,無須管理被調(diào)用者的具體實現(xiàn)
進入“共產(chǎn)主義”社會,需要斧子的人甚至無須定位工廠,“坐等”社會提供即可;調(diào)用者無須關(guān)心被調(diào)用者的實現(xiàn),無須理會工廠,等待spring依賴注入
二、設(shè)值注入
person接口: public interface person { // 定義使用斧子的方法 public void useaxe(); } spring推薦面向接口編程,這樣可更好地讓規(guī)范和實現(xiàn)分離,從而提供更好的解耦;對于一個java ee應(yīng)用,不管是dao組件還是業(yè)務(wù)邏輯組件,都應(yīng)該先定義一個接口,該接口定義了該組件應(yīng)實現(xiàn)的功能,但功能的實現(xiàn)則由其實現(xiàn)類提供
axe接口: public interface axe { // axe接口里有個砍的方法 public string chop(); }
實現(xiàn)axe: public class stoneaxe implements axe { public string chop() { return "石斧砍柴好慢s"; } }
bean.xml:
<?xml version="1.0" encoding="utf-8"?> <!-- spring配置文件的根元素,使用spring-beans-3.0.xsd語義約束 --> <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置chinese實例 --> <bean id="chinese" class="com.chinese"> <!-- 將stoneaxe注入給axe屬性 --> <property name="axe" ref="stoneaxe" /> </bean> <!-- 配置stoneaxe實例 --> <bean id="stoneaxe" class="com.stoneaxe" /> </beans>
測試類:
public class beantest { public static void main(string[] args) { // 創(chuàng)建spring容器 applicationcontext ctx = new classpathxmlapplicationcontext("bean.xml"); // 獲取chinese實例 person p = ctx.getbean("chinese", person.class); // 調(diào)用useaxe()方法 p.useaxe(); } }
spring采用xml作為配置文件,從spring2.0開始,spring即可采用dtd來定義配置文件的語義約束,也可用xml schema來定義(可利用spring配置文件的擴展性,進一步簡化spring配置;還提供了一些新的標(biāo)簽;還允許程序員開發(fā)自定義的配置文件標(biāo)簽,讓其他開發(fā)人員在spring配置文件中使用這些標(biāo)簽:通常由第三方供應(yīng)商完成);
可在spring的projects目錄的org.springframwork.beans、org.springframework.context等目錄的\src\main\resources路徑下找到各種*.xsd文件(spring配置文件的xml schema語義約束文件)
在配置文件中,spring配置bean實例通常會指定:
id :指定該bean的唯一標(biāo)識,程序通過id屬性值來訪問該bean實例
class :指定該bean的實現(xiàn)類, 此處不可再用接口 ,必須使用實現(xiàn)類spring容器用xml解析器讀取該屬性,并利用反射來創(chuàng)建該實現(xiàn)類的實例
spring會自動接管每個<bean.../>定義里的<property.../>元素定義,spring會在調(diào)用無參構(gòu)造器后、創(chuàng)建默認(rèn)bean實例后、調(diào)用對應(yīng)的setter方法為程序注入屬性值
每個bean的id屬性是該bean的唯一標(biāo)識,程序通過id屬性訪問bean,bean與bean的依賴關(guān)系也通過id屬性關(guān)聯(lián)
bean與bean間的依賴關(guān)系由spring管理,spring采用setter方法為目標(biāo)bean注入所依賴的bean,這種方式被稱為 設(shè)值注入
使用spring ioc容器的3個基本要點:
應(yīng)用程序的各組件面向接口編程
應(yīng)用程序的各組件不再由程序主動產(chǎn)生,而是由spring容器來負(fù)責(zé)產(chǎn)生、并初始化
spring采用配置文件、或annotation來管理bean的實現(xiàn)類、依賴關(guān)系,spring容器則根據(jù)配置文件、利用反射來創(chuàng)建實例,并為之注入依賴關(guān)系
三、構(gòu)造注入
在構(gòu)造實例時,已經(jīng)為其完成了依賴關(guān)系的初始化。這種利用構(gòu)造器來設(shè)置依賴關(guān)系的方式,被稱為構(gòu)造注入
public class chinese implements person { private axe axe; // 默認(rèn)的構(gòu)造器 public chinese() { } // 構(gòu)造注入所需的帶參數(shù)的構(gòu)造器 public chinese(axe axe) { this.axe = axe; } // 實現(xiàn)person接口的useaxe方法 @override public void useaxe() { // 調(diào)用axe的chop()方法 // 表明person對象依賴于axe對象 system.out.println(axe.chop()); } }
無須再提供設(shè)置axe屬性的setter方法,僅僅提供了一個帶axe屬性的構(gòu)造器,spring將通過該構(gòu)造器為chinese注入所依賴的bean實例
<?xml version="1.0" encoding="utf-8"?> <!-- spring配置文件的根元素,使用spring-beans-3.0.xsd語義約束 --> <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置chinese實例 --> <bean id="chinese" class="com.chinese"> <!-- 使用構(gòu)造注入,為chinese實例注入stoneaxe實例 --> <constructor-arg ref="stoneaxe" /> </bean> <!-- 配置stoneaxe實例 --> <bean id="stoneaxe" class="com.stoneaxe" /> </beans>
<constructor-arg.../>元素指定了一個構(gòu)造器參數(shù),該參數(shù)類型是axe,這指定spring調(diào)用chinese類里帶一個axe參數(shù)的構(gòu)造器來創(chuàng)建chinese實例,因為使用了有參數(shù)的構(gòu)造器創(chuàng)建實例,所以當(dāng)bean實例被創(chuàng)建完成后,該bean的依賴關(guān)系已經(jīng)設(shè)置完成
配置<constructor-arg.../>元素時可指定一個index屬性,用于指定該構(gòu)造參數(shù)值將作為第幾個構(gòu)造參數(shù)值;如index=“0”表明該構(gòu)造參數(shù)值將作為第一個構(gòu)造參數(shù)
執(zhí)行效果與使用設(shè)置注入時的執(zhí)行效果完全一樣。區(qū)別在于:創(chuàng)建person實例中axe屬性的時機不同-----設(shè)置注入是先通過無參數(shù)的構(gòu)造器創(chuàng)建一個bean實例,然后調(diào)用對應(yīng)的setter方法注入依賴關(guān)系;而構(gòu)造注入則直接調(diào)用有參數(shù)的構(gòu)造器,當(dāng)bean實例創(chuàng)建完成后,已經(jīng)完成了依賴關(guān)系的注入
四、兩種注入方式的對比
相比之下,設(shè)值注入有如下優(yōu)點:
與傳統(tǒng)的javabean的寫法更相似,程序開發(fā)人員更容易理解、接受、通過setter方法設(shè)定依賴關(guān)系顯得更加直觀、自然
對于復(fù)雜的依賴關(guān)系,若采用構(gòu)造注入,會導(dǎo)致構(gòu)造器過于臃腫,難以閱讀;spring在創(chuàng)建bean實例時,需同時實例化其依賴的全部實例,因而導(dǎo)致性能下降
尤其是在某些屬性可選的情況下,多參數(shù)的構(gòu)造器更加笨重
相比之下,構(gòu)造注入有如下優(yōu)點:
可在構(gòu)造器中決定依賴關(guān)系的注入順序,優(yōu)先依賴的優(yōu)先注入
對于依賴關(guān)系無需變化的bean,構(gòu)造注入更有用處;因為沒有setter方法,所有的依賴關(guān)系全部在構(gòu)造器內(nèi)設(shè)定。因此,無須擔(dān)心后續(xù)的代碼對依賴關(guān)系產(chǎn)生破壞
依賴關(guān)系只能在構(gòu)造器設(shè)定,則只有組件的創(chuàng)建者才能改變組件的依賴關(guān)系。對組件的調(diào)用者而言,組件內(nèi)部的依賴關(guān)系完全透明,更符合高內(nèi)聚的原則
一般采用以設(shè)值注入為主,構(gòu)造注入為輔的注入策略。對于依賴關(guān)系無須變化的注入,盡量采用構(gòu)造注入;而其他的依賴關(guān)系的注入,則考慮設(shè)值注入
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
- jsp+servlet實現(xiàn)文件上傳與下載功能
- EJB3.0部署消息驅(qū)動Bean拋javax.naming.NameNotFoundException異常
- 在JSP中使用formatNumber控制要顯示的小數(shù)位數(shù)方法
- 秒殺系統(tǒng)Web層設(shè)計的實現(xiàn)方法
- 將properties文件的配置設(shè)置為整個Web應(yīng)用的全局變量實現(xiàn)方法
- JSP使用過濾器防止Xss漏洞
- 在JSP頁面中動態(tài)生成圖片驗證碼的方法實例
- 詳解JSP 內(nèi)置對象request常見用法
- 使用IDEA編寫jsp時EL表達(dá)式不起作用的問題及解決方法
- jsp實現(xiàn)局部刷新頁面、異步加載頁面的方法
- Jsp中request的3個基礎(chǔ)實踐
- JavaServlet的文件上傳和下載實現(xiàn)方法
- JSP頁面的靜態(tài)包含和動態(tài)包含使用方法