- Spring+Spring MVC+MyBatis從零開始學(xué)
- 吳為勝 楊章偉
- 3497字
- 2019-11-22 18:31:38
1.2 控制反轉(zhuǎn)(IoC)與依賴注入(DI)
IoC和DI是Spring的基礎(chǔ),為什么前面提到IoC和DI描述的是同一概念呢?本節(jié)將揭曉答案。
1.2.1 什么是控制反轉(zhuǎn)(IoC)
IoC是Inversion of Control的縮寫,譯為“控制反轉(zhuǎn)”,還有的譯為“控制反向”或者“控制倒置”。
在面向?qū)ο髠鹘y(tǒng)編程方式中,獲取對(duì)象的方式通常是用new關(guān)鍵字主動(dòng)創(chuàng)建一個(gè)對(duì)象。Spring中的IoC方式對(duì)象的生命周期由Spring框架提供的IoC容器來(lái)管理,直接從IoC容器中獲取一個(gè)對(duì)象,控制權(quán)從應(yīng)用程序交給了IoC容器。
IoC理論上是借助于“第三方”實(shí)現(xiàn)具有依賴關(guān)系對(duì)象之間的解耦,如圖1.3所示,即把各個(gè)對(duì)象類封裝之后,通過(guò)IoC容器來(lái)關(guān)聯(lián)這些對(duì)象類。這樣對(duì)象與對(duì)象之間就通過(guò)IoC容器進(jìn)行聯(lián)系,而對(duì)象與對(duì)象之間沒有什么直接聯(lián)系。

圖1.3 IoC容器解耦
應(yīng)用程序在沒有引入IoC容器之前,對(duì)象A依賴對(duì)象B,那么A對(duì)象在實(shí)例化或者運(yùn)行到某一點(diǎn)的時(shí)候,自己必須主動(dòng)創(chuàng)建對(duì)象B或者使用已經(jīng)創(chuàng)建好的對(duì)象B,其中無(wú)論是創(chuàng)建還是使用已創(chuàng)建的對(duì)象B,控制權(quán)都在應(yīng)用程序自身。如果應(yīng)用程序引入了IoC容器之后,對(duì)象A和對(duì)象B之間失去了直接聯(lián)系,那么當(dāng)對(duì)象A實(shí)例化和運(yùn)行時(shí),如果需要對(duì)象B,IoC容器就會(huì)主動(dòng)創(chuàng)建一個(gè)對(duì)象B注入(依賴注入)到對(duì)象A所需要的地方。由此,對(duì)象A獲得依賴對(duì)象B的過(guò)程,由主動(dòng)行為變成被動(dòng)行為,即把創(chuàng)建對(duì)象交給了IoC容器處理,控制權(quán)顛倒過(guò)來(lái)了,這就是所謂的控制反轉(zhuǎn)。
1.2.2 什么是依賴注入(DI)
DI是Dependency Inject的縮寫,譯為“依賴注入”。所謂依賴注入,就是由IoC容器在運(yùn)行期間動(dòng)態(tài)地將某種依賴關(guān)系注入對(duì)象之中。例如,將對(duì)象B注入(賦值)給對(duì)象A的成員變量。
事實(shí)上,依賴注入(DI)和控制反轉(zhuǎn)(IoC)是對(duì)同一件事情的不同描述,從某個(gè)方面講,就是它們描述的角度不同。依賴注入是從應(yīng)用程序的角度描述,即應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源;而控制反轉(zhuǎn)是從容器的角度描述,即容器控制應(yīng)用程序,由容器反向地向應(yīng)用程序注入應(yīng)用程序所需要的外部資源。這里所說(shuō)的外部資源可以是外部實(shí)例對(duì)象,也可以是外部文件對(duì)象等。
使用IoC/DI給軟件開發(fā)帶來(lái)了多方面的益處。
(1)可維護(hù)性比較好,便于單元測(cè)試、調(diào)試程序和診斷故障。代碼中的每一個(gè)Class都可以單獨(dú)測(cè)試,彼此之間互不影響,只要保證自身的功能無(wú)誤即可,這就是組件之間低耦合或者無(wú)耦合帶來(lái)的好處。
(2)每個(gè)開發(fā)團(tuán)隊(duì)的成員都只需要關(guān)注自己要實(shí)現(xiàn)的業(yè)務(wù)邏輯,完全不用關(guān)心其他人的工作進(jìn)展,因?yàn)槟愕娜蝿?wù)跟別人沒有任何關(guān)系,你的任務(wù)可以單獨(dú)測(cè)試,不用依賴于別人的組件,再也不會(huì)扯不清責(zé)任了。所以,在一個(gè)大中型項(xiàng)目中,團(tuán)隊(duì)成員分工明確、責(zé)任明晰,很容易將一個(gè)大的任務(wù)劃分為細(xì)小的任務(wù),開發(fā)效率和產(chǎn)品質(zhì)量必將得到大幅度的提高。
(3)可復(fù)用性好,我們可以把具有普遍性的常用組件獨(dú)立出來(lái),反復(fù)應(yīng)用到項(xiàng)目中的其他部分,或者是其他項(xiàng)目,當(dāng)然這也是面向?qū)ο蟮幕咎卣鳌o@然,IoC更好地貫徹了這個(gè)原則,提高了模塊的可復(fù)用性。符合接口標(biāo)準(zhǔn)的實(shí)現(xiàn)都可以插接到支持此標(biāo)準(zhǔn)的模塊中。
(4)生成對(duì)象的方式轉(zhuǎn)為外置方式,就是把對(duì)象生成放在配置文件中進(jìn)行定義。這樣,當(dāng)我們更換一個(gè)實(shí)現(xiàn)子類將會(huì)變得很簡(jiǎn)單,只要修改配置文件就可以了,完全具有熱插拔的特性。
1.2.3 IoC/DI的實(shí)現(xiàn)
Spring框架的主要功能是通過(guò)其核心容器來(lái)實(shí)現(xiàn)的。Spring框架提供的兩種核心容器分別是BeanFactory和ApplicationContext。IoC/DI通常有setter(設(shè)置)注入和構(gòu)造方法注入兩種實(shí)現(xiàn)方式。
注意
如前所述,依賴注入(DI)和控制反轉(zhuǎn)(IoC)是對(duì)同一件事情的不同描述。所以這里講的IoC/DI實(shí)現(xiàn)方式其實(shí)就是DI實(shí)現(xiàn)方式。
1.Spring核心容器
Spring框架的兩個(gè)最基本和最重要的包是org.springframework.beans.factory(該包中的主要接口是BeanFactory)和org.springframework.context(該包中的主要接口是ApplicationFactory)。
Spring IoC框架的主要組件有Beans、配置文件applicationContext.xml、BeanFactory接口及其相關(guān)類、ApplicationContext接口及其相關(guān)類。
(1)Beans是指項(xiàng)目中提供業(yè)務(wù)功能的Bean,即容器要管理的Bean。Beans就是一個(gè)常見的JavaBean、Java類。
(2)在Spring中對(duì)Bean的管理是在配置文件中進(jìn)行的。在Spring容器內(nèi)編輯配置文件管理Bean又稱為Bean的裝配,實(shí)際上裝配就是告訴容器需要哪些Bean,以及容器是如何使用IoC將它們配合起來(lái)的。Bean的配置文件是一個(gè)XML文件,可以命名為applicationContext.xml或其他,一般習(xí)慣使用applicationContext.xml。
配置文件包含Bean的id、類、屬性及其值,包含一個(gè)<beans>元素和數(shù)個(gè)<bean>子元素。Spring IoC框架可根據(jù)Bean的id從Bean配置文件中取得該Bean的類,并生成該類的一個(gè)實(shí)例對(duì)象,繼而從配置文件中獲得該對(duì)象的屬性和值。常見applicationContext.xml配置文件格式如下:

(3)BeanFactory采用了工廠設(shè)計(jì)模式,即Bean容器模式,負(fù)責(zé)讀取Bean的配置文件,管理對(duì)象的生成、加載,維護(hù)Bean對(duì)象與Bean對(duì)象之間的依賴關(guān)系,負(fù)責(zé)Bean的生命周期。對(duì)于簡(jiǎn)單的應(yīng)用程序來(lái)說(shuō),使用BeanFactory就已經(jīng)足夠管理Bean了,在對(duì)象的管理上可以獲得許多便利性。
org.springframework.beans.factory.BeanFactory是一個(gè)頂級(jí)接口,包含管理Bean的各種方法。Spring框架也提供了一些實(shí)現(xiàn)該接口的類。
org.springframework.beans.factory.xml.XmlBeanFactory是BeanFactory常用的實(shí)現(xiàn)類,根據(jù)配置文件中的定義裝載Bean。要?jiǎng)?chuàng)建XmlBeanFactory,需要傳遞一個(gè)FileInputStream對(duì)象,該對(duì)象把XML文件提供給工廠。代碼可以寫成:
BeanFactory factory=new XmlBeanFactory( new FileInputStream("applicationContext.xml "));
BeanFactory的常用方法如下:
- getBean(String name):可根據(jù)Bean的id生成該Bean的對(duì)象。
- getBean(String name,Class requiredType):可根據(jù)Bean的id和相應(yīng)類生成該Bean的對(duì)象。
(4)ApplicationContext接口提供高級(jí)功能的容器,基本功能與BeanFactory很相似,但它還有以下功能:
- 提供訪問資源文件更方便的方法。
- 支持國(guó)際化消息。
- 提供文字消息解析的方法。
- 可以發(fā)布事件,對(duì)事件感興趣的Bean可以接收到這些事件。
ApplicationContext接口的常用實(shí)現(xiàn)類有以下3個(gè)。
- FileSystemXmlApplicationContext:從文件系統(tǒng)中的XML文件加載上下文中定義的信息。
- ClassPathXmlApplicationContext:從類路徑中的XML文件加載上下文中定義的信息,把上下文定義的文件當(dāng)成類路徑資源。
- XmlWebApplicationContext:從Web系統(tǒng)中的XML文件加載上下文中定義的信息。
其中,F(xiàn)ileSystemXmlApplicationContext和ClassPathXmlApplicationContext的代碼編寫如下:
ApplicationContext context=new FileSystemXmlApplicationContext("d:/applicationContext.xml "); ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml ");
第1種使用文件系統(tǒng)的方式來(lái)查詢配置文件,此時(shí)applicationContext.xml文件位于D盤下。第2種使用類路徑來(lái)查詢配置文件,此時(shí)applicationContext.xml文件位于項(xiàng)目的src目錄底下。
FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的區(qū)別是:FileSystemXmlApplicationContext只能在指定的路徑中查詢applicationContext.xml配置文件,而ClassPathXmlApplicationContext可以在整個(gè)類路徑中查詢applicationContext.xml。
2.IoC/DI實(shí)現(xiàn)方式
如前所述,依賴注入(DI)和控制反轉(zhuǎn)(IoC)是對(duì)同一件事情的不同描述。依賴注入的作用是在使用Spring框架創(chuàng)建對(duì)象時(shí),動(dòng)態(tài)地將其所依賴的對(duì)象注入Bean組件中,其實(shí)現(xiàn)方式通常有兩種:一種是屬性setter()方法注入;另一種是構(gòu)造方法注入。具體介紹如下:
- 屬性setter()方法注入:IoC容器使用setter()方法注入被依賴的實(shí)例。通過(guò)調(diào)用無(wú)參構(gòu)造器或無(wú)參靜態(tài)工廠方法實(shí)例化Bean后,調(diào)用該Bean的setter()方法,即可實(shí)現(xiàn)基于setter()方法的依賴注入。該方式簡(jiǎn)單、直觀,而且容易理解,所以Spring的設(shè)置注入被大量使用。
- 構(gòu)造方法注入:IoC容器使用構(gòu)造方法注入被依賴的實(shí)例。基于構(gòu)造方法的依賴注入通過(guò)調(diào)用帶參數(shù)的構(gòu)造方法來(lái)實(shí)現(xiàn),每個(gè)參數(shù)代表著一個(gè)依賴。
【示例1-1】下面以常用的setter()方法注入的方式為例講解Spring容器在應(yīng)用中是如何實(shí)現(xiàn)依賴注入的。
(1)在Eclipse集成開發(fā)環(huán)境中創(chuàng)建一個(gè)名為chapter01的動(dòng)態(tài)Web項(xiàng)目,將Spring的4個(gè)基礎(chǔ)包以及commons-logging的JAR包復(fù)制到lib目錄中,并發(fā)布到類路徑下,如圖1.4所示。

圖1.4 創(chuàng)建項(xiàng)目導(dǎo)入包
(2)在src目錄下創(chuàng)建一個(gè)com.ssm.ioc_di包,并在包中創(chuàng)建接口UserDao,然后在接口中定義一個(gè)login()方法,如文件1.1所示。
文件1.1 UserDao.java

(3)在com.ssm.ioc_di包中創(chuàng)建UserDao接口的實(shí)現(xiàn)類UserDaoImpl,該類需要實(shí)現(xiàn)接口中的login()方法,并在方法中編寫一條輸出語(yǔ)句,如文件1.2所示。
文件1.2 UserDaoImpl.java

(4)在src目錄下創(chuàng)建Spring的配置文件applicationContext.xml,并在配置文件中創(chuàng)建一個(gè)id為UserDao的Bean,如文件1.3所示。
文件1.3 applicationContext.xml

在文件1.3中,第01~05代碼中包含一些約束信息,其中“spring-beans-4.3.xsd”與Spring的版本4.3相對(duì)應(yīng);第07行代碼表示在Spring容器中創(chuàng)建一個(gè)id為UserDao的Bean實(shí)例,其中class屬性用于指定需求實(shí)例化Bean的類。
注意
Spring配置文件的名稱可以自定義,通常默認(rèn)命名為applicationContext.xml。
(5)在com.ssm.ioc_di包中創(chuàng)建測(cè)試類IoC,并在類中編寫main()方法及實(shí)現(xiàn)IoC的代碼,如文件1.4所示。
文件1.4 IoC.java

程序執(zhí)行后,控制臺(tái)輸出結(jié)果如圖1.5所示。從中可以看出,控制臺(tái)成功輸出了UserDaoImpl類中的輸出語(yǔ)句。在文件1.4的main()方法中,并沒有通過(guò)new關(guān)鍵字來(lái)創(chuàng)建UserDao接口的實(shí)現(xiàn)類對(duì)象,而是通過(guò)Spring容器來(lái)獲取的實(shí)現(xiàn)類對(duì)象,這就是Spring IoC(控制反轉(zhuǎn))的工作機(jī)制。

圖1.5 運(yùn)行結(jié)果
(6)在com.ssm.ioc_di包中創(chuàng)建接口UserService,并編寫一個(gè)login()方法,如文件1.5所示。
文件1.5 UserService.java

(7)在com.ssm.ioc_di包中創(chuàng)建接口UserService的實(shí)現(xiàn)類UserServiceImpl,在類中聲明userDao屬性,并添加屬性的setter()方法;同時(shí)編寫login()方法。具體代碼如文件1.6所示。
文件1.6 UserServiceImpl.java

(8)在配置文件applicationContext.xml中創(chuàng)建一個(gè)id為userService的Bean,該Bean用于實(shí)例化UserServiceImpl類的信息,并將name為userDao的實(shí)例注入userService中,其代碼如下所示。

在上述代碼中,<property>是<bean>元素的子元素,用于調(diào)用Bean實(shí)例中的setUserDao()方法完成屬性賦值,從而實(shí)現(xiàn)依賴注入。其name屬性表示Bean實(shí)例中的相應(yīng)屬性名,ref屬性用于指定其屬性值。
(9)在com.ssm.ioc_di包中創(chuàng)建測(cè)試類DI,如文件1.7所示。
文件1.7 DI.java

此時(shí)運(yùn)行效果如圖1.6所示。從中可以看出,使用Spring容器通過(guò)UserService實(shí)現(xiàn)類中的login()方法調(diào)用了UserDao實(shí)現(xiàn)類中的login()方法,并輸出了結(jié)果。這就是Spring容器屬性setter注入的方式,也是實(shí)際開發(fā)中常用的一種方式。

圖1.6 運(yùn)行結(jié)果
為了方便讀者理解,圖1.7給出整個(gè)項(xiàng)目目錄結(jié)構(gòu)。

圖1.7 項(xiàng)目目錄結(jié)構(gòu)
- Oracle Exadata性能優(yōu)化
- Web Scraping with Python
- Mastering Yii
- Web Application Development with MEAN
- Spring核心技術(shù)和案例實(shí)戰(zhàn)
- C語(yǔ)言開發(fā)基礎(chǔ)教程(Dev-C++)(第2版)
- 動(dòng)手學(xué)數(shù)據(jù)結(jié)構(gòu)與算法
- 多模態(tài)數(shù)據(jù)分析:AGI時(shí)代的數(shù)據(jù)分析方法與實(shí)踐
- Practical Predictive Analytics
- C語(yǔ)言程序設(shè)計(jì)與應(yīng)用實(shí)驗(yàn)指導(dǎo)書(第2版)
- Python數(shù)據(jù)科學(xué)實(shí)踐指南
- INSTANT Lift Web Applications How-to
- Spring Boot 2+Thymeleaf企業(yè)應(yīng)用實(shí)戰(zhàn)
- Raspberry Pi開發(fā)實(shí)戰(zhàn)
- Python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)(第2版)