javascript
Spring入门 IOC
JAVA就業(yè)套餐課:https://edu.csdn.net/combo/detail/1230
課程說明
一、?技術(shù)概覽
輕量級的企業(yè)應(yīng)用開發(fā)越來越受到廣大Java應(yīng)用開發(fā)者的追捧,而Spring框架又是輕量級容器的杰出代表。由于Spring的使用日漸廣泛,因此已有許多應(yīng)用服務(wù)器(WAS)的應(yīng)用采用了Spring框架。本書先介紹Spring的依賴注入、面向切面編程、Spring抽象JDBC框架,然后介紹三大框架的整合。
Spring框架的核心思想我們可以用兩個字來描述,那就是“解耦”。應(yīng)用程序的各個部分之間(包括代碼內(nèi)部和代碼與平臺之間)盡量形成一種松耦合的結(jié)構(gòu),使得應(yīng)用程序有更多的靈活性。應(yīng)用內(nèi)部的解耦主要通過一種稱為控制反轉(zhuǎn)(IOC)的技術(shù)來實現(xiàn)。控制反轉(zhuǎn)的基本思想就是本來由應(yīng)用程序本身來主動控制的調(diào)用等邏輯轉(zhuǎn)變成由外部配置文件來被動控制。由于控制反轉(zhuǎn)的概念相對比較廣泛,很多應(yīng)用服務(wù)器實際上也實現(xiàn)了不同程度的控制反轉(zhuǎn)技術(shù),只是這些應(yīng)用服務(wù)器對應(yīng)用程序的侵入性太強(qiáng)。因此Martin Fowler專門寫了一篇文章討論控制反轉(zhuǎn)這個概念,并提出一個描述更為準(zhǔn)確的概念,叫依賴注入(Dependency Injection)。
Spring框架中的各個部分都充分使用了這種依賴注入的技術(shù)實現(xiàn),從而給應(yīng)用以最大的靈活度。實際上,這種依賴注入的參數(shù)化應(yīng)用控制并不是Spring的首創(chuàng),比如IBM的多渠道應(yīng)用整合平臺(Branch Transformation Toolkit,BTT)很早就采用了這種外部參數(shù)化控制的技術(shù)。BTT中的“對象工廠”與Spring框架中的BeanFactory也有著異曲同工之妙。
Spring框架另外一個比較重要的技術(shù)是它對于面向切面的編程(AOP)的支持。隨著應(yīng)用復(fù)雜度的逐漸上升和對應(yīng)用靈活性要求的提高,IT邏輯和業(yè)務(wù)邏輯盡量分離的呼聲也越來越高。AOP技術(shù)作為實現(xiàn)這種分離的一種比較好的途徑而越來越受到大家的重視。Spring提供的是一種動態(tài)AOP實現(xiàn),也即通過代理模式動態(tài)地在目標(biāo)對象的方法前后插入相應(yīng)的處理代碼。應(yīng)用程序與底層應(yīng)用服務(wù)器平臺的解耦也可以借助AOP技術(shù)來實現(xiàn)。Spring內(nèi)置的AOP支持是一種錦上添花的功能。它使得一些本來必須由容器支持的功能,比如事務(wù)控制可以脫離開容器運行,從而達(dá)到“瘦身”的目的。這也是為什么Spring框架常被人成為輕量級容器的一個原因。
JDBC基于SQL,不要求我們掌握其他框架的查詢語言,簡單易學(xué),因此學(xué)習(xí)成本低。另外,在使用 JDBC 時,可以更細(xì)致地調(diào)整數(shù)據(jù)訪問的性能。JDBC 還允許我們利用數(shù)據(jù)庫的特有功能,而其他框架可能不鼓勵甚至禁止使用它們。 但是JDBC并不完美,無論是執(zhí)行查詢,更新,插入還是刪除操作, JDBC都要求我們正確地管理連接和語句,還要處理可能拋出的SQLException,及時的釋放資源。這顯然造成了大量的代碼重復(fù)。這似乎印證了 Pareto 法則:只有 20% 的代碼是查詢操作所必需的,而80%代碼是樣板代碼。Spring抽象JDBC框架基于模板設(shè)計模式,將上述必須都又和核心業(yè)務(wù)無關(guān)的樣板代碼封裝到模板方法中,以簡化開發(fā),讓編程人員可以將精力集中在核心業(yè)務(wù)之上。
Spring框架可以與許多已有的框架技術(shù)結(jié)合使用。J2EE技術(shù)應(yīng)用的一個重要特點是相關(guān)的開源社區(qū)非常活躍。Web應(yīng)用的不同層次都有非常多優(yōu)秀的開源框架存在。比如Web層的Struts,ORM映射層的Hibernate等。Spring框架并不重新發(fā)明輪子,它的出現(xiàn)不是為了替代這些已有的框架。相反,Spring框架在設(shè)計上可以獨立構(gòu)建應(yīng)用或者結(jié)合已有的框架一起構(gòu)建應(yīng)用。另外一個值得指出的地方是Spring框架的幾大模塊之間相互耦合度很小,因此Spring框架的使用可以根據(jù)實際需要選其部分模塊循序漸進(jìn)的使用,而非必須統(tǒng)統(tǒng)照搬。
?
本章簡介
Spring是用于簡化企業(yè)應(yīng)用程序開發(fā)過程的開源框架,屬于輕量級的控制反向 (IOC,即 Inversion of control)和面向切面編程 (AOP,即 Aspect Oriented ?Programming)的容器框架。本章以 Spring的起源及背景為起點,介紹Spring的工作原理。然后以組裝計算機(jī)為貫穿案例介紹Spring Bean的封裝機(jī)制、Spring對Bean的管理,分別使用設(shè)值注入、構(gòu)造注入、自動注入等方式組裝Bean。最后通過一個示例介紹集合屬性的使用。
?
1.1?Spring簡介
1.1.1?Spring 的歷史
Spring的基礎(chǔ)架構(gòu)起源于2000年早期,創(chuàng)始人為畢業(yè)于悉尼大學(xué)的音樂學(xué)博士Rod Johnson。2002 年后期,Rod Johnson 發(fā)布了《Expert One-on-One J2EE 設(shè)計與開發(fā)》一書,在書中,他對傳統(tǒng)的J2EE技術(shù)(以EJB為核心)日益臃腫和低效提出了質(zhì)疑,他覺得應(yīng)該有更簡潔的做法,于是提出了Interface21,也就是Spring框架的雛形。他還隨書提供了Interface21 開發(fā)包以實現(xiàn)初步框架的開發(fā),Interface21 即書中思想的具體實現(xiàn)。Rod Johnson 以 Interface21 開發(fā)包為基礎(chǔ),通過改造與擴(kuò)充將其升級為更加開放、清晰、全面、高效的開發(fā)框架——Spring。2003年2月,Spring 框架正式成為開源項目,并發(fā)布于SourceForge中。后期隨著數(shù)百甚至上千開發(fā)者貢獻(xiàn)各自的經(jīng)驗,Spring 在改進(jìn)與加強(qiáng)中變得日益強(qiáng)大,開發(fā)者的熱心與投入使 Spring 社區(qū)十分活躍。
?
Spring框架的發(fā)展與成熟,離不開日復(fù)一日為 Spring 社區(qū)默默地做出偉大貢獻(xiàn)的會員們。
1.1.1?Spring工作原理
Spring是一種通過JavaBean配置應(yīng)用程序的方法。我們不需要通過new關(guān)鍵詞創(chuàng)建對象,而是在配置文件中配置JavaBean。當(dāng)對象與對象之間有依賴關(guān)系的時候,我們也只需要在配置文件中把依賴關(guān)系體現(xiàn)出來,這些被配置的Bean將會納入Spring管理,放置于Spring容器中。我們只需要寫很少量的代碼便可得到Spring容器,并且從Spring容器中得到配置的JavaBean。這種解決依賴性的方法即控制反轉(zhuǎn) (IOC,即Inversion of Control)或者依賴注入(Dependency Injection),從技術(shù)上來說,即使用某種容器組織相互依賴的對象。除了IOC之外,Spring還可以將分散在系統(tǒng)的公共代碼統(tǒng)一組織起來,在運行的時候加入到系統(tǒng)中,這就是AOP(面向切面編程)。
1.1.2?Spring框架簡介
Spring 是用于簡化企業(yè)應(yīng)用程序開發(fā)過程的開源框架,屬于輕量級的控制反轉(zhuǎn) (IOC)和面向切面編程 (AOP,即 Aspect Oriented Programming)的容器框架,解決了J2EE開發(fā)中許多常見的問題。我們需要了解一下Spring中的一些名詞:
(1)輕量級:以所占大小及系統(tǒng)開銷分析,Spring屬于輕量級。整個Spring框架可以打包為 1M左右的JAR包,且系統(tǒng)開銷較小。同時,Spring為非侵入式,若系統(tǒng)基于Spring開發(fā),其所含的對象一般不依賴于Spring的類。
(2)IOC: IOC使對象被動接受依賴類,而并非主動獲取。也就是說,告訴 Spring“你”是什么,“你”需要什么對象,然后Spring會在系統(tǒng)運行到適當(dāng)?shù)臅r候,把“你”要的對象主動給“你”,同時也把“你”交給其他需要“你”的對象。所有的類的創(chuàng)建、銷毀都由Spring來控制,控制對象生存周期的不再是引用它的對象,而是 Spring。對于某個具體的對象而言,使用 Spring 之前是它控制其他對象,現(xiàn)在是所有對象都被 Spring 控制,所以叫控制反轉(zhuǎn)。在系統(tǒng)運行中,動態(tài)的向某個對象提供它所需要的其他對象,這一點是通過DI(依賴注入)實現(xiàn)的。
(3)AOP:面向切面編程(也叫面向方面編程),關(guān)注系統(tǒng)的橫向切面。通俗點說就是把代碼“切開”,然后在需要的位置上動態(tài)加入公共代碼。比如日志或者事務(wù)支持。
(4)容器:Spring 是一個包含且管理系統(tǒng)對象生命周期和配置的容器,在使用 Spring 應(yīng)用開發(fā)的時候,幾乎所有的 JavaBean 對象都需要 Spring 來“盛放”。Spring 容器的作用是管理對象。
(5)Spring框架:Spring能夠通過簡單的組件組合為復(fù)雜的系統(tǒng)。Spring框架為分層架構(gòu),由7個定義良好的模塊組成,各模塊構(gòu)建于核心容器之上,核心容器定義了創(chuàng)建、配置及管理Bean的方式,如圖 1.1.2 所示。
?
轉(zhuǎn)存失敗重新上傳取消
?
?
圖 1.1.2 中,各模塊 (或組件)可以單獨存在,也可以與其他一個或多個模塊聯(lián)合實現(xiàn)。各模塊的功能如下:
(1)Spring Core:核心容器,用于提供 Spring框架的基本功能,其主要組件為 BeanFactory,是工廠模式的實現(xiàn)。BeanFactory使用反向控制(IOC)模式將應(yīng)用程序的配置及依賴性規(guī)范與實際應(yīng)用程序代碼分開。
(2)Spring Context:核心模塊的BeanFactory使Spring成為容器,上下文 (Context)模塊使其成為框架。此模塊擴(kuò)展了BeanFactory的概念,增加了對國際化(I18N,即Internationalization)消息、事件的傳播以及驗證的支持;同時,此模塊提供諸多企業(yè)服務(wù),如電子郵件、JNDI訪問、EJB集成、遠(yuǎn)程以及時序調(diào)度(Scheduling)服務(wù),支持對模版框架 (如Velocity、FreeMarker)的集成。
(3)Spring AOP:通過配置管理特性,Spring AOP模塊將面向切面編程功能集成至框架中,使Spring框架管理的任何對象均支持AOP。Spring AOP模塊向基于 Spring的應(yīng)用程序中的對象提供事務(wù)管理服務(wù)。此模塊無需依賴于EJB組件,可以使用 Spring AOP將聲明式事務(wù)管理集成至應(yīng)用程序中。
(4)Spring DAO:JDBC DAO抽象層提供了意義重大的異常層次結(jié)構(gòu),簡化了錯誤處理過程并極大地減少了需要編寫的異常代碼 (如打開或關(guān)閉連接),運用此結(jié)構(gòu)可以管理異常、處理不同數(shù)據(jù)庫供應(yīng)商拋出的錯誤消息。Spring DAO的面向JDBC異常遵從通用的 DAO異常層次結(jié)構(gòu)。
(5)Spring ORM:Spring框架中插入了若干ORM框架,提供ORM的對象關(guān)系工具,包括JDO、Hibernate以及iBatis SQL Map,都遵從Spring的通用事務(wù)及DAO異常層次結(jié)構(gòu)。
(6)Spring Web:Web上下文模塊建立在應(yīng)用程序上下文模塊之上,向基于Web 的應(yīng)用程序提
供上下文,因而Spring框架支持與Struts集成。同時,Web模塊簡化了請求的處理過程以及將請求參數(shù)綁定至域?qū)ο蟮墓ぷ鳌?/p>
(7)Spring MVC:MVC 框架是一個全功能的構(gòu)建 Web 應(yīng)用程序的 MVC 實現(xiàn)。通過策略接口,MVC框架變?yōu)楦叨瓤膳渲?#xff0c;它容納了大量視圖技術(shù),包括JSP、Velocity、Tiles、iText及POI等。
創(chuàng)建Spring項目可以使用MyEclipse開發(fā)工具,先創(chuàng)建項目,然后增加Spring支持。操作過程如圖1.1.3和1.1.4所示。
?
轉(zhuǎn)存失敗重新上傳取消
?
轉(zhuǎn)存失敗重新上傳取消
?
圖1.1.4中類庫的添加只包含核心類庫,使用Spring控制反轉(zhuǎn)的時候一般不需要添加其他類庫。
1.1??Spring Bean封裝機(jī)制
1.1.1?Spring Bean?
Spring以Bean的方式管理所有的組件,J2EE 的全部組件都使用Bean管理。在Spring中,
除了標(biāo)準(zhǔn)的JavaBean,其他任何對象和組件都可以作為Bean。
應(yīng)用中各層的對象均由Spring管理,對象以Bean方式存在。Spring負(fù)責(zé)創(chuàng)建Bean的實例并管理其生命周期,Bean運行于Spring的容器。Spring上下文是生產(chǎn)Bean的工廠,Bean是Spring工廠生產(chǎn)的實例。Spring產(chǎn)生工廠時,需要確定每個Bean的實現(xiàn)類;Bean實例的使用者面向接口,因此無須關(guān)心Bean實例的實現(xiàn)類。Spring工廠負(fù)責(zé)維護(hù)Bean實例的實例化,使用者則無須關(guān)心。
Bean的定義通常使用XML配置文件,正確定義的Bean 由Spring提供實例化以及依賴關(guān)系的注入等。最簡單的Spring配置文件代碼如示例1.1所示。
示例1.1
?
?
?
<?xml version="1.0" encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> </beans>?
Spring的schemaLocation詳細(xì)規(guī)定了Spring配置文件的合法元素、各元素出現(xiàn)的先后順序、各元素的合法子元素以及合法屬性等。
增加對實體對象管理的Bean,配置文件代碼如示例1.2所示。
?
示例1.2
?
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean id="user" class="com.hr.entity.UserVo" abstract="false" lazy-init="default" autowire="default" /> </beans>?
上述代碼中定義了UserVo對象。XML中Bean節(jié)點的部分屬性如下:
?
<bean id="beanId" name ="beanName" class="beanClass" parent="parentBean" abstract="true | false" scope="prototype|singleton" lazy-init="true | false | default" autowire="no | byName | by Type | constructor | auto detect |default"> </ bean>?
?
?
以上屬性的含義如表 1-1-l所示。
表1-1-1 Bean屬性
| 屬性 | 含義 |
| id | Bean的唯一標(biāo)識名,必須為合法的XML ID,在整個XML文檔中唯一,如果沒有特殊需要,我們在標(biāo)識一個Bean的時候,一般推薦使用id。 |
| name | 用于為id創(chuàng)建一個或多個別名,可以是任意字母或者符號,多個別名之間以逗號或空格分隔。 |
| class | 用于定義類的全限定名(包名加類名),Spring在創(chuàng)建對象的時候需要用到此屬性,因此該屬性不能指定接口。 |
| parent | 子類Bean定義其所引用的父類Bean,繼承父類的所有屬性。值得一提的是,在寫代碼的時候,即便是兩個類之間沒有繼承關(guān)系,我們同樣可以使用該屬性。 |
| abstract | 用于定義Bean是否為抽象Bean,默認(rèn)為false,表示此Bean將不會被實例化。一般用于父類Bean,可以在子類繼承的時候使用。 |
| scope | 用于定義Bean的作用域,singleton表示在每個Spring IoC容器中一個bean定義對應(yīng)一個對象實例,即Spring使用單例模式獲取實例。prototype表示一個bean定義對應(yīng)多個對象實例,即非單例模式 |
| lazy-init | 用于定義Bean是否實現(xiàn)初始化,默認(rèn)為default。若為true,將在BeanFactory啟動時初始化所有Singleton Bean;若為false,則在Bean請求時創(chuàng)建Singleton Bean |
| autowire | 用于定義Bean的自動裝配方式,默認(rèn)為default,包括不使用自動裝配功能、通過Bean的屬性名實現(xiàn)自動裝配、通過Bean的類型實現(xiàn)自動裝配、通過Bean類的反省 (Introspection)機(jī)制決定選擇使用constructor或者byType。 |
1.1.1?Application Context
Spring包括兩種不同的容器:BeanFactory 和 ApplicationContext。BeanFactory提供基本的IOC支持;ApplicationContext則基于BeanFactory,提供應(yīng)用程序框架服務(wù)。Spring提供了BeanFactory與 ApplicationContext 的多個實現(xiàn)。
ApplicationContext包括 BeanFactory 的全部功能,除非應(yīng)用程序?qū)π阅芤蠛芨邥r才考慮BeanFactory,其他情況下建議優(yōu)先使用ApplicationContext。
應(yīng)用中出現(xiàn)多個配置文件時,應(yīng)采用BeanFactory的子接口ApplicationContext創(chuàng)建 BeanFactory的實例。ApplicationContext通常使用ClassPathXmlApplicationContext實現(xiàn)類,該類以classpath路徑下的XML配置文件創(chuàng)建ApplicationContext。使用該類創(chuàng)建Spring容器的代碼如示例1.3所示。
示例1.3
// 搜索classpath路徑,以classpath路徑下的applicationContext.xml創(chuàng)建對象ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
在實際應(yīng)用中,會將Spring配置分別放在不同的配置文件中,如果一個應(yīng)用中有兩個配置文件application.xml和bean.xml,則創(chuàng)建容器實例的方法如示例1.4所示。
示例1.4
// 搜索classpath路徑,以classpath路徑下的
//applicationContext.xml和bean.xml創(chuàng)建ApplicationContext。?
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml","bean.xml"});
ApplicationContext接口包括以下 3 個重要方法:
(1)containsBean(String name)方法。
//判斷Spring容器是否包含ID為?user的Bean
boolean flag=applicationContext.containsBean("user");
(2)getBean(String name)方法。
//返回ID為?user的Bean
UserVo userBean= (UserVo)applicationContext.getBean("user");
該方法的功能是從Spring容器中獲取一個對象,該方法的參數(shù)可以是Spring配置文件中Bean的id或者name,如果name指定了多個標(biāo)識,只需傳入一個標(biāo)識。
(3)getType(String name)方法。
//返回:ID為?user 的類型
Class beanType= applicationContext.getType("user");
以下示例將從Spring容器中獲取一個user對象,并輸出user的信息。
示例1.5
// 以classpath路徑下的applicationContext.xml創(chuàng)建Spring容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "applicationContext.xml"); // 從Spring容器中獲取ID為user的Bean UserVo user = (UserVo) applicationContext.getBean("user"); // 封裝姓名和年齡 user.setName("張三豐"); user.setAge(100); // 輸出該Bean的信息 user.printInfo();從示例1.5我們發(fā)現(xiàn)Bean的創(chuàng)建將交給Spring管理,我們要做的就是在Spring配置文件中對Bean配置即可。
1.1?Spring DI/IOC
1.1.1?什么是Spring的依賴注入
我們先看看什么叫依賴。在生活中,依靠別人或者別的事物而不能獨立或者自立,我們稱為依賴。那么在應(yīng)用中,什么叫依賴呢?
依賴指的是兩個實例之間的關(guān)系。其中一個實例是獨立的,另一個實例是非獨立的(依賴的),它依靠另外一個實例。比如計算機(jī)對象,它包含主機(jī)對象和顯示器對象。如果沒有主機(jī)對象或者顯示器對象,則計算機(jī)對象就是不完整的,不能正常使用,我們就說計算機(jī)對象依賴于主機(jī)對象和顯示器對象。
那么什么是注入呢?
計算機(jī)對象離不開主機(jī)對象和顯示器對象,程序在運行過程中,我們必須給計算機(jī)對象提供它所需要的主機(jī)對象和顯示器對象,把主機(jī)對象和顯示器對象像“打針”一樣提供給計算機(jī)對象,這個過程就叫做注入。
也就是說,如果一個對象需要另外一個對象才能正常使用,我們在程序運行的時候,給該對象提供它所需要的對象,這就是“依賴注入”。我們知道,Spring將會管理幾乎所有的Bean對象,而對象與對象之間可能存在依賴關(guān)系,在程序運行過程中,Spring把我們所需要的對象都拼裝好,這就是Spring的依賴注入。
在傳統(tǒng)的 Java設(shè)計中,當(dāng) Java實例的調(diào)用者創(chuàng)建被調(diào)用的 Java實例時,要求被調(diào)用的 Java類出現(xiàn)在調(diào)用者的代碼中,二者之間無法實現(xiàn)松耦合。工廠模式則對此進(jìn)行了改進(jìn),使調(diào)用者無須關(guān)心被調(diào)用者的具體實現(xiàn)過程,只要獲得符合某種標(biāo)準(zhǔn) (接口)的實例即可使用。其調(diào)用的代碼面向接口編程,支持調(diào)用者與被調(diào)用者解耦,因此工廠模式得以大范圍地使用。但在工廠模式中,調(diào)用者需要自行定位工廠,與特定工廠耦合,所以僅在一定程度上實現(xiàn)了調(diào)用者與被調(diào)用者的解耦。Spring 的出現(xiàn)使調(diào)用者無須自行定位工廠,當(dāng)程序運行至需要被調(diào)用者時,系統(tǒng)將自動提供被調(diào)用者實例。事實上,調(diào)用者與被調(diào)用者均由 Spring管理,二者之間的依賴關(guān)系由Spring提供。
下面以計算機(jī)的組裝為例來演示此過程。計算機(jī)由主機(jī)和顯示器組成,我們需要輸出一臺計算機(jī)的信息,比如該計算機(jī)由什么型號的主機(jī)、什么型號的顯示器組成。下面我們一起來分析并設(shè)計該系統(tǒng)。
主機(jī)設(shè)計成一個類,該類(MainFrame)中有一個方法,用來輸出主機(jī)的型號,如示例1.6所示。
示例1.6
?
private String modelType;//型號 // 輸出主機(jī)信息 public void printMainFrameInfo() {System.out.println("主機(jī)型號:" + modelType); } //setter & getter顯示器設(shè)計成一個接口,該接口(Display)聲明一個輸出顯示器信息的方法,如示例1.7所示。
示例1.7
public void printDisplayInfo();
顯示器接口有兩個實現(xiàn),分別是三星顯示器和LG顯示器,如示例1.8所示。
示例1.8
?
//三星顯示器 public class SamSungDisplay implements Display {public void printDisplayInfo() {System.out.println("顯示器:三星顯示器");} } //LG顯示器 public class LgDisplay implements Display {public void printDisplayInfo() {System.out.println("顯示器:LG顯示器 ");} }?
計算機(jī)由主機(jī)和顯示器組成,因此計算機(jī)類(Computer)有兩個屬性,一個是主機(jī),一個是顯示器,如示例1.9所示。
示例1.9
?
private MainFrame mainFrame;// 主機(jī) private Display display;// 顯示器接口// 輸出計算機(jī)配置信息public void printComputerInfo() {System.out.println("計算機(jī)配置如下:");mainFrame.printMainFrameInfo();// 輸出主機(jī)信息display.printDisplayInfo();// 輸出顯示器信息} //setter & getter?
根據(jù)需求,計算機(jī)依賴于主機(jī)和顯示器,因此可以使用Spring的依賴注入來實現(xiàn)。我們需要在配置文件中配置三星顯示器、LG顯示器和計算機(jī)。
三星顯示器的配置如示例1.10所示。
示例1.10
<bean id="samsung" class="s3spring.ch01.computer.mainframe.SamSungDisplay" />
LG顯示器的配置如示例1.11所示。
示例1.11
<bean id="lg" class="s3spring.ch01.computer.mainframe.LgDisplay" />
主機(jī)的modelType屬性是直接量,直接量指的是基本數(shù)據(jù)類型和字符串,對直接量的注入,我們可以使用<value/>元素來指定。主機(jī)的配置如示例1.12所示。
示例1.12
<bean id="mainFrame" class="s3spring.ch01.computer.mainframe.MainFrame">
<property name="modelType" value="三星高配主機(jī)"/>
</bean>
目前為止,我們已經(jīng)把主機(jī)對象和顯示器對象交給了Spring,接下來,我們需要讓Spring管理計算機(jī)對象,計算機(jī)對象對主機(jī)對象和顯示器對象有依賴,我們需要用Spring的依賴注入來實現(xiàn)。
Spring的依賴注入對調(diào)用者與被調(diào)用者幾乎無任何要求。依賴注入通常分為設(shè)值注入、構(gòu)造注入、自動注入等。
1.1.1?設(shè)值注入方式
設(shè)值注入是指通過setter方法傳入被調(diào)用者的實例,所以Bean的屬性要求有對應(yīng)的setter和getter方法,此方式因簡單直觀而得以廣泛使用。
計算機(jī)的屬性為主機(jī)和顯示器,我們可以使用<ref/>元素來指定,該元素用來將bean中指定屬性的值設(shè)置為對容器中的另外一個bean的引用,采用設(shè)值注入方法的配置如示例1.13所示。
示例1.13
?
<!-- 計算機(jī)的配置 --> <bean id="computer" class="s3spring.ch01.computer.Computer"><!-- 設(shè)值注入主機(jī),name對應(yīng)computer的屬性,ref對應(yīng)所依賴的bean--><property name="mainFrame" ref="mainFrame"/><!-- 設(shè)值注入三星顯示器 --><property name="display" ref="samsung"/> </bean>?
如果mainFrame、samsung和computer這三個Bean的配置在同一個配置文件,我們也可以把<property name=“mainframe”?ref=“mainframe”/>更改為
<property name=“mainframe”?><ref local=“mainframe”/></property>。如果mainFrame、samsung和computer不在同一個配置文件中,則需要更改為<property name=“mainframe”><ref bean=“mainframe”/></property>,無論是local還是bean都可以指定為一個所依賴的Bean的id或者name。
由配置文件可知,Spring在管理Bean時非常靈活。Bean與 Bean 間的依賴關(guān)系在配置文件中組織,并非在代碼中編寫。Spring通過配置文件精確地為每個Bean注入實例化的屬性。因此,配置文件Bean的class元素不能為接口,只能為實現(xiàn)類。
Spring 自動管理Bean定義property元素,在執(zhí)行無參的構(gòu)造方法和創(chuàng)建默認(rèn)的Bean 的實例后,Spring會調(diào)用對應(yīng)的setter方法注入屬性值。property定義的屬性值不再由 Bean創(chuàng)建或管理,而是被動接受Spring的注入。Bean的 ID 屬性為其唯一標(biāo)識,程序通過 ID 屬性對Bean進(jìn)行訪問,Bean與 Bean間的依賴關(guān)系也由 ID屬性實現(xiàn)。設(shè)值注入的測試代碼如示例1.14所示。
示例1.14
?
ApplicationContext applicationContextContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Computer computer=(Computer)applicationContextContext.getBean("computer");//輸出計算機(jī)的配置信息 computer.printComputerInfo();?
目前為止,我們成功的從Spring容器中獲取了計算機(jī)對象。Spring的依賴注入使得我們在維護(hù)項目的時候變的非常簡單,如果把顯示器更改LG顯示器,我們并不需要修改任何代碼,修改配置文件即可實現(xiàn),配置文件的修改如下。
<property name="display" ref="lg"/>
1.1.1?構(gòu)造注入方式
構(gòu)造注入是指通過構(gòu)造方法完成依賴關(guān)系的注入,并非 setter方法。將以上設(shè)值注入按構(gòu)造注入重新實現(xiàn),則需要在Computer中增加帶參構(gòu)造方法,如示例1.15所示。
示例1.15
?
public Computer(MainFrame mainFrame, Display display) {this.mainFrame = mainFrame;this.display = display; }?
Spring配置文件需要修改,修改后如示例1.16所示。
示例1.16
?
<bean id="computer" class="s3spring.ch01.computer.Computer"> //構(gòu)造注入主機(jī)和顯示器<constructor-arg index="0" ref="mainFrame" /><constructor-arg index="1" ref="samsung" /> </bean>?
采用構(gòu)造注入方式時,Spring容器將會在程序運行時調(diào)用Computer的帶參構(gòu)造方法來創(chuàng)建實例,該帶參構(gòu)造有兩個參數(shù),index指的是第幾個參數(shù),ref指傳入該參數(shù)的Bean。其執(zhí)行效果與設(shè)值注入完全相同,區(qū)別在于屬性的創(chuàng)建時機(jī)不同。設(shè)值注入創(chuàng)建默認(rèn)的Bean實例后,調(diào)用對應(yīng)的 setter方法注入依賴關(guān)系。構(gòu)造注入則在創(chuàng)建 Bean實例時完成依賴關(guān)系的注入。
1.1.1?自動注入
Spring容器可以自動注入(autowire)相互協(xié)作bean之間的關(guān)聯(lián)關(guān)系。因此,如果可能的話,可以自動讓Spring通過檢查BeanFactory中的內(nèi)容,來替我們指定bean的協(xié)作者(其他被依賴的bean)。由于autowire可以針對單個bean進(jìn)行設(shè)置,因此可以讓有些bean使用autowire,有些bean不采用。autowire的方便之處在減少或者消除屬性或構(gòu)造器參數(shù)的設(shè)置,這樣可以簡化配置文件,如果直接使用property和constructor-arg注入依賴的話,那么將總是覆蓋自動裝配,在xml配置文件中,可以在<bean/>元素中使用autowire屬性指定,如表1-1-2所示。
表1-1-2 autowire屬性
| 模式 | ?說明 |
| no | 不使用自動裝配。必須通過ref元素指定依賴,這是默認(rèn)設(shè)置。由于顯式指定協(xié)作者可以使配置更靈活、更清晰,因此對于較大的部署配置,推薦采用該設(shè)置。 |
| byName | 根據(jù)屬性名自動裝配。此選項將檢查容器并根據(jù)名字查找與屬性完全一致的bean,并將其與屬性自動裝配。 |
| byType | 如果容器中存在一個與指定屬性類型相同的bean,那么將與該屬性自動裝配。如果存在多個該類型的bean,那么將會拋出異常,并指出不能使用byType方式進(jìn)行自動裝配。若沒有找到相匹配的bean,則屬性不會被設(shè)置。 |
如果把配置文件修改為示例1.17所示,則采用自動裝配來組裝計算機(jī)。
示例1.17
?
<bean id="computer" class="s3spring.ch01.computer.Computer" autowire="byName"><property name="display" ref="samsung"></property> </bean>?
由于使用byName自動裝配,容器會檢查ID和Computer的兩個屬性mainFrame以及display名稱匹配的Bean并注入,由于存在ID為mainFrame的Bean,所以Computer的mainFrame屬性被自動注入,而display是顯式注入,所以覆蓋了display屬性的自動注入。
1.1.1?集合屬性
通過<list/>、<set/>、<map/>及<props/>元素可以定義和設(shè)置與Java Collection類型對應(yīng)List、Set、Map及Properties的值。示例1.18中定義TestBean類,該類有四個屬性,分別是List、Map、Properties、Set類型。
?
private List list; private Map map; private Properties prop; private Set set;?
對TestBean類的配置如示例1.19所示。
示例1.19
<bean id="testBean" class="com.bean.TestBean"> <!--list屬性的配置 --><property name="list"><list><value>v1</value><value>v2</value></list></property><!-- map屬性的配置 --> <property name="map"><map><entry key="k1"><value>v1</value></entry><entry key="k2"><value>v2</value></entry></map></property> <!-- prop屬性的配置 --><property name="prop"><props><prop key="k1">str1</prop><prop key="k2">str2</prop></props></property><!-- set屬性的配置 --><property name="set"><set><value>v1</value><value>v2</value></set></property> </bean>程序運行的時候,我們從Spring容器中獲取testBean實例,該實例的四個集合屬性中將會包含指定的對象。
1.1.1?Spring 注入方式的比較
1. 設(shè)值注入的特點
對于習(xí)慣了傳統(tǒng)JavaBean開發(fā)的程序員而言,通過setter方法設(shè)定依賴關(guān)系更加直觀自然。當(dāng)依賴關(guān)系(或繼承關(guān)系)較復(fù)雜時,構(gòu)造注入方式的構(gòu)造函數(shù)相當(dāng)龐大,且需要在構(gòu)造函數(shù)中設(shè)定所有依賴關(guān)系,此時使用設(shè)值注入方式則簡單快捷。除此之外,某些第三方類庫要求組件必須提供默認(rèn)的構(gòu)造函數(shù) (如 Struts 中的 Action),此時構(gòu)造注入方式的依賴注入機(jī)制會突顯局限性,難以完成預(yù)期的功能,必須通過設(shè)值注入實現(xiàn)。
2. 構(gòu)造注入的優(yōu)點
在構(gòu)造期即創(chuàng)建完整、合法的對象,構(gòu)造注入無疑是此Java設(shè)計原則的最佳響應(yīng)者。
而且構(gòu)造注入還避免了編寫繁瑣的setter方法,所有的依賴關(guān)系都在構(gòu)造函數(shù)中設(shè)定,使依賴關(guān)系集中呈現(xiàn),可讀性增加。由于不存在 setter方法,而是在構(gòu)造時由容器一次性設(shè)定依賴關(guān)系。因此,組件在創(chuàng)建之后即處于相對穩(wěn)定狀態(tài),無須擔(dān)心上層代碼在調(diào)用過程中執(zhí)行setter方法時破壞組件之間的依賴關(guān)系。對于 Singleton模式組件,這種破壞將對整個系統(tǒng)產(chǎn)生重大的影響。通過構(gòu)造注入,可以在構(gòu)造函數(shù)中決定依賴關(guān)系的注入順序。對于大量依賴外部服務(wù)的組件而言,依賴關(guān)系的獲取順序至關(guān)重要。
1.1?基于注解的容器配置
基于注解(Annotation)的配置有越來越流行的趨勢,Spring順應(yīng)這種趨勢,從2.5版起提供了完全基于注解配置 Bean、裝配 Bean 的功能,您可以使用基于注解的 Spring IoC 替換原來基于 XML 的配置。
我們?nèi)匀皇褂糜嬎銠C(jī)組裝的例子來講解基于注解的IOC容器配置語法。
1.1.1?使用?@Component注解配置bean
只需要在類的定義語句上方加上一個 @Component 注解,就將該類定義為一個bean了,請看示例1.20,將Computer、MainFrame、SamSungDisplay、LgDisplay配置成bean:
示例1.20?
?
@Component public class Computer { private MainFrame mainFrame;// 主機(jī)private Display display;// 顯示器接口// 輸出計算機(jī)配置信息public void printComputerInfo() {System.out.println("計算機(jī)配置如下:");mainFrame.printMainFrameInfo();// 輸出主機(jī)信息display.printDisplayInfo();// 輸出顯示器信息} }@Component public class MainFrame {private String modelType;// 型號// 輸出主機(jī)信息public void printMainFrameInfo() {System.out.println("主機(jī)型號:" + modelType);} } @Component public class SamSungDisplay implements Display {public void printDisplayInfo() {System.out.println("顯示器:三星顯示器");} } @Component public class LgDisplay implements Display {public void printDisplayInfo() {System.out.println("顯示器:LG顯示器 ");} }?
如上代碼所示,我們通過@Component注解分別將Computer、MainFrame、SamSungDisplay、LgDisplay配置成了bean(注意,在后面的示例中,我們會默認(rèn)已經(jīng)配置了上述4個bean)。bean名稱默認(rèn)為將第一個字母轉(zhuǎn)換為小寫的類名。如Computer類的bean默認(rèn)名稱是computer。上述代碼的等價xml配置是:
<bean id="samSungDisplay" class="s3spring.ch01.computer.annotation.SamSungDisplay" /> <bean id="lgDisplay" class="s3spring.ch01.computer.annotation.LgDisplay" /> <bean id="mainFrame" class="s3spring.ch01.annotation.computer.MainFrame"/> <bean id="computer" class="s3spring.ch01.annotation.computer.Computer"/>@Component注解唯一的一個可選參數(shù)是value,用于指定bean的名稱(即id值,所以必須是唯一的),如示例1.21所示,聲明Computer類為一個bean,id為computer:
示例1.21
?
@Component(value=”computer”)
public class Computer { …… }
可以省略參數(shù)名稱,簡寫為:
@Component(”computer”)
public class Computer { …… }
?
Spring還提供了更加細(xì)化的用于定義bean的注解形式:@Repository、@Service、@Controller,它們分別對應(yīng)數(shù)據(jù)訪問層bean,業(yè)務(wù)層bean,和視圖層bean。目前版本中,這些注解與@Component的語義是一樣的,完全通用,在Spring以后的版本中可能會給它們追加更多的語義。所以,我們推薦使用@Repository、@Service、@Controller來替代@Component。
1.1.1?掃描?@Component標(biāo)注的類
在使用?@Component?注解后,Spring 容器必須啟用類掃描機(jī)制,以找到所有使用@Component標(biāo)注的類,將它們作為bean來管理。從Spring 2.5開始, 對 context 命名空間進(jìn)行了擴(kuò)展,提供了這一功能,請看下面的配置,引入context命名空間,并掃描bean:
示例1.22?
?
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><context:component-scan base-package="s3spring.ch1.annotation.computer"/></beans>?
?
Spring 容器在初始化時會掃描 <context:component-scan/> 標(biāo)簽的 base-package 屬性指定的類包及其遞歸子包中所有的類,找出 @Component標(biāo)注的類,將它們配置成bean。然后,我們就可以通過Spring 容器的工廠方法獲取并使用bean了。請看示例1.23,通過Spring容器獲取Computer bean:
示例1.23?
?
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Computer c = ac.getBean(Computer.class);?
執(zhí)行示例1.23,如果沒有拋出任何異常,即表示能夠正常獲得bean。
1.1.1?使用?@Scope注解配置bean 的作用域
@Scope用于定義bean的作用域,singleton表示在每個Spring IoC容器中一個bean定義對應(yīng)一個對象實例,即Spring使用單例模式獲取實例。prototype表示一個bean定義對應(yīng)多個對象實例,即非單例模式,我們常稱作多例。默認(rèn)情況下,Spring bean 的作用域為 singleton。示例1.24 將Computer類配置成一個“多例”的bean:
示例1.24
@Component
@Scope(value="prototype")
public class Computer { …… }
可以省略參數(shù)名稱,簡寫為:
@Component
@Scope("prototype")
public class Computer { …… }
1.1.2?使用?@Autowired注解
從Spring 2.5版本起, Spring引入了?@Autowired?注解,它可以對類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,以完成自動裝配依賴的工作。
來看一下示例1.25,使用?@Autowired?為成員變量自動注入依賴:
示例1.25?
?
@Component @Scope("prototype") public class Computer {@Autowiredprivate MainFrame mainFrame;// 主機(jī) @Autowiredprivate Display display;// 顯示器接口// 輸出計算機(jī)配置信息public void printComputerInfo() {System.out.println("計算機(jī)配置如下:");mainFrame.printMainFrameInfo();// 輸出主機(jī)信息display.printDisplayInfo();// 輸出顯示器信息} }?
?
?
將?@Autowired?標(biāo)注于實例屬性mainFrame和display上面,Spring將直接采用?Java 反射機(jī)制獲取?mainFrame?和?display?屬性的類型,再根據(jù)類型查找“唯一”匹配的bean來進(jìn)行依賴自動注入.(如果bean的類型與屬性的類型相同,或者bean的類型是屬性類型的子類型或接口實現(xiàn),則類型匹配)。一般推薦將?@Autowired放在setter方法之上。
由于和mainFrame屬性類型匹配的bean只有MainFrame 一個,所以自動裝配依賴可以順利進(jìn)行。但是和display?屬性的類型匹配的bean有兩個(SamSungDisplay和LgDisplay都實現(xiàn)了Display接口,而display屬性類型正是?Display),此時Spring 應(yīng)用容器不知道該用哪一個bean為Computer的display屬性注入依賴值,從而導(dǎo)致創(chuàng)建Computer bean失敗,拋出BeanCreationException。異常信息如下:
Error creating bean with name 'computer': Injection of autowired dependencies failed;
……
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [s3spring.ch1.annotation.computer.Display] is defined: expected single matching bean but found 2: [lgDisplay, samSungDisplay]
那么如何解決根據(jù)類型返回多個bean導(dǎo)致的創(chuàng)建bean失敗的問題呢?答案是指定需要注入的bean 的名稱。要為Computer bean的display屬性注入三星顯示器,就需要告訴Spring我們需要的是符合Display類型,且id為“samSungDisplay”的bean。
指定依賴bean名稱的方法有兩種:
l?把屬性名稱做為依賴bean名稱
l?使用?@Qualifier 注解明確指定依賴bean的名稱,我們將在1.4.5小節(jié)講解
將display屬性改名為“samSungDisplay”,當(dāng)Spring 應(yīng)用容器根據(jù)屬性類型返回多個bean時,會繼續(xù)以反射的方式獲得屬性的名稱?“samSungDisplay”,從而找到同名的bean。請看示例1.26:
示例1.26
?
@Component @Scope("prototype") public class Computer {@Autowiredprivate MainFrame mainFrame;// 主機(jī) @Autowiredprivate Display samSungDisplay;// 顯示器接口// 輸出計算機(jī)配置信息public void printComputerInfo() {System.out.println("計算機(jī)配置如下:");mainFrame.printMainFrameInfo();// 輸出主機(jī)信息samSungDisplay.printDisplayInfo();// 輸出顯示器信息} }?
我們也可以使用setter方法,構(gòu)造方法和其它的方法,根據(jù)參數(shù)類型和名稱來注入依賴bean,請看示例1.27:
示例1.27
@Component @Scope("prototype") public class Computer {private MainFrame mainFrame;// 主機(jī)private Display display;// 顯示器接口 @Autowired //構(gòu)造函數(shù)的變量名與容器中的對象名保持一致,否則需要@Qualifier指定注入 Bean 的名稱public Computer(MainFrame mainFrame, Display samSungDisplay) {super();this.mainFrame = mainFrame;this.display = samSungDisplay;}…… }?
1.1.1?使用?@Qualifier注解為自動注入指定依賴bean的名稱
?
Spring 允許我們通過?@Qualifier?注解指定注入 Bean 的名稱。
示例1.28
?
@Component @Scope("prototype") public class Computer {@Autowiredprivate MainFrame mainFrame;// 主機(jī) @Autowired @Qualifier("samSungDisplay")private Display display;// 顯示器接口…… }?
使用@Qualifier注解為方法指定要注入的依賴bean的名稱。
示例1.29?
?
@Component @Scope("prototype") public class Computer {private MainFrame mainFrame;// 主機(jī)private Display display;// 顯示器接口 @Autowired public Computer( MainFrame mainFrame, @Qualifier("samSungDisplay") Display display ) {super();this.mainFrame = mainFrame;this.display = display;}…… }?
總結(jié),使用 @Autowired ?注解自動裝配依賴的過程如下:
1)?首先根據(jù)屬性的類型(或方法、構(gòu)造方法參數(shù)的類型)在Spring 應(yīng)用容器中查找類型匹配的bean
2)?如果沒有類型匹配的bean,拋出BeanCreationException;如果只有一個,則注入依賴,完成自動裝配;如果不只一個,則繼續(xù)執(zhí)行步驟3;
3)?如果通過?@Qualifier指定了bean 名稱,則從所有符合類型的bean中返回指定的bean,完成自動裝配;如果沒有通過?@Qualifier制定bean 名稱,則通過反射技術(shù)獲取當(dāng)前屬性的名稱作為bean 名稱返回指定的bean,完成自動裝配;
1.1.1?使用?@Resource 注解注入依賴
Spring 不但支持自己定義的@Component、@Scope、?@Autowired、?@Qualifier注解,還支持幾個由?JSR-250 規(guī)范定義的注解,它們分別是?@Resource、@PostConstruct?以及?@PreDestroy。
@Resource?的作用相當(dāng)于?@Autowired,只不過?@Autowired?默認(rèn)按 byType 自動注入,面?@Resource?默認(rèn)按 byName 自動注入罷了。@Resource?有兩個屬性是比較重要的,分別是 name 和 type,Spring 將@Resource?注解的 name 屬性解析為 Bean 的名字,而 type 屬性則解析為 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機(jī)制使用 byName 自動注入策略。
@Resource執(zhí)行結(jié)果,即使name不一致也可以注入,就是按照類型了。
請看示例1.30:
示例1.30
?
@Component @Scope("prototype") public class Computer { @Resourceprivate MainFrame mainFrame;//采用屬性名稱作為依賴bean的名稱@Resource(name=”samSungDisplay”)private Display display;// 采用@Resource注解name參數(shù)值作為依賴bean的名稱…… }1.1.1?@PostConstruct 和?@PreDestroy
?
Spring 容器中的?Bean 是有生命周期的,Spring 允許在?Bean 在初始化完成后以及?Bean 銷毀前執(zhí)行特定的操作。JSR-250 為初始化之后/銷毀之前指定執(zhí)行方法定義了兩個注解類,分別是 @PostConstruct 和 @PreDestroy,這兩個注解只能應(yīng)用于方法上。標(biāo)注了 @PostConstruct 注解的方法將在類實例化后調(diào)用,而標(biāo)注了 @PreDestroy 的方法將在類銷毀之前調(diào)用。請看示例1.31:
示例1.31
?
@Component @Scope("prototype") public class Computer {…… @PostConstruct public void postConstruct1(){System.out.println("執(zhí)行postConstruct1");}@PreDestroy public void preDestroy1(){System.out.println("執(zhí)行preDestroy1"); }}?
執(zhí)行示例1.31,在bean被初始化時,會輸出“執(zhí)行postConstruct1”。在bean被銷毀時,會輸出“執(zhí)行preDestroy1”。我們通常可以在標(biāo)注了 @PostConstruct的方法中完成一些資源初始化的工作,在?標(biāo)注了 @PreDestroy 的方法中完成一些釋放資源的操作。注意的是@PreDestroy要在容器關(guān)閉之前,所以要使用以下代碼初始化容器:
?
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext”); //釋放容器資源,會調(diào)用@PreDestroy注解的方法 context.close();[新添加] 或者 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext”); context.destory();?
?
?
執(zhí)行示例1.31,在bean被初始化時,會輸出“執(zhí)行postConstruct1”。在bean被銷毀時,會輸出“執(zhí)行preDestroy1”。我們通常可以在標(biāo)注了 @PostConstruct的方法中完成一些資源初始化的工作,在?標(biāo)注了 @PreDestroy 的方法中完成一些釋放資源的操作。注意的是@PreDestroy要在容器關(guān)閉之前,所以要使用以下代碼初始化容器:
?
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext(“applicationContext”);
//釋放容器資源,會調(diào)用@PreDestroy注解的方法
context.close();
或者
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(“applicationContext”);
context.destory();
注意:如果Bean類配置了@Scope("prototype")則不會調(diào)用@PreDestory注解的方法.
本章總結(jié)
?
?Spring框架
?Spring 是用于簡化企業(yè)應(yīng)用程序開發(fā)過程的開源框架
?Spring 的核心是一個控制反轉(zhuǎn)容器
?Spring 提供了AOP實現(xiàn)
?Spring 提供了常用WEB框架整合的支持
?Spring 提供了對ORM技術(shù)整合的支持
?Spring Bean 封裝機(jī)制
?Spring以Bean的方式管理所有的組件
?Spring包括兩種不同的容器:BeanFactory 和 ApplicationContext
?依賴注入
?設(shè)置注入
?構(gòu)造器注入
?自動注入
?集合注入
?基于注解配置應(yīng)用容器
?@Component
?@scope
?@Autowired
?@Qualifier
? @Resource
?@PostConstruct?和?@PreDestroy。
任務(wù)實訓(xùn)部分
?
1: 使用Spring獲取Bean對象
訓(xùn)練技能點
?Spring Bean的管理
?Bean的配置
?Spring容器的創(chuàng)建
需求說明
我們在配置電腦的時候,不同型號對應(yīng)不同的硬盤,先要求編寫Spring程序,能夠根據(jù)不同的硬盤型號,得到不同的硬盤實例。
實現(xiàn)思路
(1)?定義硬盤接口。
(2)?定義不同型號的硬盤實現(xiàn)類。
(3)?在Spring配置文件中配置硬盤實現(xiàn)類。
(4)?創(chuàng)建ClassPathXmlApplicationContext并獲取硬盤對象。
關(guān)鍵代碼
(1)?定義硬盤接口。
public interface HardDisk {
public void printHardDiskInfo();//輸出硬盤信息
}
(2)?定義兩個硬盤實現(xiàn)類。
//希捷硬盤
public class SeaGateHardDisk implements HardDisk {
public void printHardDiskInfo() {
System.out.println("希捷160G硬盤");
}
}
//三星硬盤
public class SamSungHardDisk implements HardDisk {
public void printHardDiskInfo() {
System.out.println("三星250G硬盤");
}
}
(3)?使用<bean>標(biāo)簽對三星硬盤、希捷硬盤進(jìn)行配置,三星硬盤的標(biāo)識為samSungHardDisk,希捷硬盤的標(biāo)識為seaGateHardDisk,配置文件中的的關(guān)鍵代碼如下所示。
//三星硬盤的配置
<bean id="samSungHardDisk" class="bean.SamSungHardDisk"/>
//希捷硬盤的配置
<bean id="seaGateHardDisk" class="bean.SeaGateHardDisk"/>
(4)?編寫測試類測試。
ApplicationContext context =
???????new ClassPathXmlApplicationContext("applicationContext.xml");
// 獲取三星硬盤
HardDisk hardDisk = (HardDisk) context.getBean("samSungHardDisk");
hardDisk.printHardDiskInfo();
// 獲取希捷硬盤
hardDisk = (HardDisk) context.getBean("seaGateHardDisk");
hardDisk.printHardDiskInfo();
2:?設(shè)值注入實現(xiàn)打印機(jī)組裝
訓(xùn)練技能點
?Spring設(shè)值注入
需求說明
打印機(jī)由墨盒和紙張組成,墨盒分彩色墨盒和黑白墨盒,紙張有A3紙和A4紙。我們在打印的時候,經(jīng)常切換墨盒和紙張,現(xiàn)要求編寫一個Spring程序組裝一臺打印機(jī)并輸出組成該打印機(jī)的紙張和墨盒信息。
實現(xiàn)思路
(1)?定義墨盒接口(Ink)和紙張接口(Paper),Ink接口中聲明方法 printInkInfo()輸出墨盒信息,Paper接口中聲明方法printPaperInfo()輸出紙張信息。
(2)?定義打印機(jī)類(Printer)。打印機(jī)類中包含方法printerInfo(),輸出打印機(jī)的配置。
(3)?定義墨盒實現(xiàn)類(ColorInk和GrayInk)和紙張實現(xiàn)類(A3Paper和A4Paper)。
(4)?Spring配置文件,先配置彩色墨盒和黑白墨盒,然后配置A3紙和A4紙,最后配置打印機(jī),使用設(shè)值方式在打印機(jī)中注入墨盒和紙張。
(5)?編寫測試類輸出打印機(jī)信息。
?
3:?使用構(gòu)造注入重新組裝打印機(jī)
訓(xùn)練技能點
?Spring構(gòu)造注入
需求說明
任務(wù)實訓(xùn)1的基礎(chǔ)上,使用構(gòu)造注入重新組裝打印機(jī)。
實現(xiàn)思路
(1)?在Printer類中增加帶參構(gòu)造方法。
(2)?修改配置文件,改為構(gòu)造注入。
4:?使用ByName自動注入重新組裝打印機(jī)
訓(xùn)練技能點
?Spring自動注入
需求說明
升級實訓(xùn)任務(wù)3,打印機(jī)包括硬盤、顯示器、電源,要求使用Spring程序組裝打印機(jī),并輸出打印機(jī)的配置信息。
實現(xiàn)思路
(1)?添加電源類Power,該類有一個方法printPowerInfo(),用于輸出電源信息。
(2)?修改配置文件為ByName自動注入。
(3)?編寫測試類進(jìn)行測試。
關(guān)鍵代碼
(1)?編寫電源類:
public class Power {
private String type;// 電源型號
public void printPowerInfo() {
System.out.println("電源:" + type);
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
(2)?在打印機(jī)類中增加屬性:
private Power power;// 電源
public Power getPower() {
return power;
}
public void setPower(Power power) {
this.power = power;
}
(3)?修改Spring配置文件,添加以下配置:
<bean id="power" class="bean.Power">
??<property name="type">
<value>長城</value>
??</property>
</bean>
<bean id="printer" class="bean.Printer" autowire="byName">
5:?使用Spring管理部門對象
訓(xùn)練技能點
?list元素的使用
需求說明
某公司銷售部有三名員工,請編寫Spring程序輸出銷售部的部門名稱、該部門所有員工的員工姓名和員工地址。要求使用集合元素實現(xiàn)該需求。
實現(xiàn)思路
(1)?創(chuàng)建EmpVo類和DeptVo類。
(2)?DeptVo類中定義List集合屬性emps。
(3)?配置Spring文件。
(4)?編寫測試類,輸出給部門詳細(xì)信息。
關(guān)鍵代碼
(1)?員工信息包括員工姓名和員工地址,我們可以在員工類中增加員工姓名屬性和員工地址屬性。
public class EmpVo {
private String name;//員工名稱
private String adress;//員工地址
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
}
部門中可以包含多個員工,我們可以在部門類中增加兩個屬性,分別是部門名稱和部門員工集合,關(guān)鍵代碼如下所示。
private String deptName;// 部門名稱
private List emps;// 部門員工集合
(2)?編寫Spring配置文件,對部門的配置如下所示。
<bean id="dept" class="bean.DeptVo">
<property name="deptName">
<value>銷售部</value>
</property>
<property name="emps">
<list>
<ref bean="emp0" />
<ref bean="emp1" />
</list>
????????</property>
</bean>
鞏固練習(xí)
?
一.選擇題
1. 有關(guān)Spring,以下說法錯誤的是()。
???A. Spring是一個輕量級框架。
???B. Spring提供依賴注入容器、AOP實現(xiàn)、DAO/ORM支持、Web集成等功能。
???C. Spring的目標(biāo)是使現(xiàn)有的技術(shù)變的更加好用。
???D. Spring不支持自動注入。
2. 以下關(guān)于依賴注入的選項中,說法正確的是()。
???A. 依賴注入的目標(biāo)是在代碼之外管理程序組件間的依賴關(guān)系
???B. 依賴注入就是“面向切面編程”
???C. 依賴注入是面向?qū)ο蠹夹g(shù)的替代品
???D. 依賴注入會增大程序的規(guī)模
3. 關(guān)于IOC的理解,以下說法正確的是()。
???A. 控制反轉(zhuǎn)
???B. 對象被動接受依賴類
???C. 對象主動尋找依賴類
???D. 必須使用接口
4. 下列選項中,屬于Spring依賴注入方式的是()。
???A. set方法注入
???B. 構(gòu)造方法注入
???C. get方法注入
???D. 接口注入
5. 以下關(guān)于在Spring中配置Bean的id屬性的說法中,正確的是()。
???A. id屬性值可以重復(fù)
???B. id屬性值不可以重復(fù)
???C. id屬性是必需的,沒有id屬性時會報鍺
D. id屬性不是必需的
6. 下列哪一個注解用于聲明一個bean()。
???A. @Resource
???B. @Component
???C. @Scope
D. @Qualifier
7. 下列關(guān)于使用注解來進(jìn)行依賴注入的描述正確的是()。
???A. ?@Autowired?默認(rèn)按 byType 自動注入依賴
B. @Autowired?默認(rèn)按 byName 自動注入依賴
???C. @Resource?默認(rèn)按?byType 自動注入依賴
D. @Resource?默認(rèn)按?byName 自動注入依賴
?
二.操作題
1.某程序系統(tǒng)中有如下幾個類。
public class Equip {//裝備
private String name;//裝備名稱
private String type;//裝備類型,頭盔、鎧甲等
private Long speedPlus;//速度增效
private Long attackPlus;//攻擊增效
private Long defencePlus;//防御增效
//Getters & Setters
. . .
}
public class Player {// 玩家
private Equip armet;// 頭盔
private Equip loricae;// 鎧甲
private Equip boot;// 靴子
private Equip ring;// 指環(huán)
// Getters & Setters
public void updateEquip(Equip equip) {
if ("頭盔".equals(equip.getType())) {
System.out.println(armet.getName() + "升級為" + equip.getName());
this.armet = equip;
}
???. . .
}
}
根據(jù)以上信息,使用Spring DI配置一個擁有如下裝備的玩家,如表1-3-1所示。
表1-3-1 裝備信息
| 裝備 | 戰(zhàn)神頭盔 | 連環(huán)鎖子甲 | 波斯追風(fēng)靴 | 藍(lán)魔指環(huán) |
| 速度增效 | 2 | 6 | 8 | 8 |
| 攻擊增效 | 4 | 4 | 2 | 12 |
| 防御增效 | 6 | 15 | 3 | 2 |
?
2.編寫一個程序,實現(xiàn)對員工信息的CURD,要求DAO類中編寫5個方法:
get(int id)、save(EmpVo emp)、delete(int id)、update(EmpVo emp)、findAll()方法,編寫B(tài)IZ類,BIZ類中一個方法:doUpdate(EmpVo emp),方法功能為指定編號的員工如果存在,把薪水在原有基礎(chǔ)上增加500;如果不存在,則提示該員工不存在,更改失敗。使用Spring管理DAO和BIZ,并從Spring容器中獲取BIZ實例進(jìn)行測試。
提示:創(chuàng)建EmpBiz接口和實現(xiàn)類,在實現(xiàn)類中增加DAO屬性。
public class EmpBizImpl implements EmpBiz {
private EmpDAO empDAO;
public void doUpdate(EmpVo emp)
{
//調(diào)用DAO類方法操作
}
//setter & getter
}
3.?自從世界上出現(xiàn)飛機(jī)以來,飛機(jī)的結(jié)構(gòu)形式雖然在不斷改進(jìn),飛機(jī)類型不斷增多,但到目前為止,除了極少數(shù)特殊形式的飛機(jī)之外,大多數(shù)飛機(jī)都是由下面六個主要部分組成,即:兩支機(jī)翼、一個機(jī)身、兩支尾翼、一個起落裝置、一個操縱系統(tǒng)和四個動力裝置。請使用Spring的依賴注入組裝一架飛機(jī)。
提示:
編寫六個接口,分別對應(yīng)機(jī)翼、機(jī)身、尾翼、起落裝置、操縱系統(tǒng)、動力裝置。在飛機(jī)類中增加兩個機(jī)翼屬性,分別表示左機(jī)翼和右機(jī)翼,尾翼和動力裝置采用相同的處理。
?
4.移動公司某營業(yè)點需要一個會員賬戶管理系統(tǒng),該系統(tǒng)需要實現(xiàn)以下功能
(1)顯示所有會員賬戶信息。
(2)為指定的會員賬戶充值。
(3)刪除指定的賬戶。
(4)禁用或者啟用指定的賬戶。
(5)根據(jù)狀態(tài)查詢會員賬戶。
系統(tǒng)首頁參考界面如圖1.3.1所示:
轉(zhuǎn)存失敗重新上傳取消
圖1.3.1 賬戶管理系統(tǒng)首頁
當(dāng)點擊“充值”時,如果該會員狀態(tài)為禁用狀態(tài),則給出“禁用狀態(tài)的會員不能充值”提示,點擊“啟用”可以將會員狀態(tài)更改為“啟用狀態(tài)”。參考界面如圖1.3.2。
轉(zhuǎn)存失敗重新上傳取消
圖1.3.2 充值失敗提示
會員充值界面如圖1.3.3所示。
轉(zhuǎn)存失敗重新上傳取消
圖1.3.3會員充值界面
當(dāng)會員充值成功之后,系統(tǒng)跳轉(zhuǎn)至首頁,顯示當(dāng)前最新會員信息。
點擊“刪除”將刪除會員信息,刪除成功之后,系統(tǒng)首頁將顯示最新的會員信息。
請根據(jù)以上需求完成以下內(nèi)容:
該系統(tǒng)的數(shù)據(jù)庫設(shè)計,DAO層的編寫,業(yè)務(wù)邏輯層的實現(xiàn),并使用Spring管理業(yè)務(wù)邏輯層對象。
提示:
在業(yè)務(wù)邏輯層增加DAO類屬性,使用Spring在Biz中注入DAO,編寫測試代碼,對Biz中方法進(jìn)行測試,不需要WEB層的開發(fā)。
總結(jié)
以上是生活随笔為你收集整理的Spring入门 IOC的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子商务之 网店客服中心服务用语规范
- 下一篇: Spring面向切面编程