IoC好处
IoC(Inversion of Control?,控制反轉)也稱為依賴注入(Dependency Injection),作為Spring的一個核心思想,是一種設計對象之間依賴關系的原則及其相關技術。
IoC是什么?
?
高內聚低耦合可以說是軟件技術形態的終極目標。用學術界的話來說,軟件的兩個本質特性就是構造性和演化性,高內聚低耦合的設計能夠讓構造和演化都更加高效,比如:
- 開發更方便組織分工
- 代碼更容易進行復用
- 更容易進行測試
- 軟件演化有更好的靈活性,能快速響應需求變化,維護代價更小
軟件設計各種技術的出現,無一不是朝著這個終極目標的努力。面向對象、基于組件(學術界稱為構件)的軟件開發、面向切面編程(AOP)、Java近些年流行的模塊化方法(比如OSGi技術)等等,這些方法和技術的出現,無外乎都是為了讓軟件更加高內聚低耦合。與此同時,各路大神還提出各種軟件設計原則和模式,來規范我們的軟件形態。我們今天談的IoC也是其中的一個大招。
?
IoC(Inversion of Control?,控制反轉)也稱為依賴注入(Dependency Injection),作為Spring的一個核心思想,是一種設計對象之間依賴關系的原則及其相關技術。
?
先來看看字面上怎么來解釋:當一個對象創建時,它所依賴的對象由外部傳遞給它,而非自己去創建所依賴的對象(比如通過new操作)。因此,也可以說在對象如何獲取它的依賴對象這件事情上,控制權反轉了。這便不難理解控制反轉和依賴注入這兩個名字的由來了。
一個場景
?
上面的解釋聽起來還是有點晦澀,讓我們來看看具體的例子吧!
有個土豪老板,我們經常要出差,因此經常要訂機票。定機票呢,可以通過去哪兒網訂票,也可以通過攜程訂票。
我們馬上可以想到可以通過三個類來表達這個場景,Boss,QunarBookingService,CtripBookingService。當然了,我們還應該提供一個BookingService接口,作為QunarBookingService,CtripBookingService的公共抽象。面向接口編程是面向對象設計的基本原則,如果這都不了解,趕緊先回去看GoF的《設計模式》第一章!
BookingService.java
package com.tianmaying.iocdemo;public interface BookingService {void bookFlight(); }QunarBookingService.java
package com.tianmaying.iocdemo;public class QunarBookingService implements BookingService {public void bookFlight() {System.out.println("book fight by Qunar!");} }CtripBookingService.java
package com.tianmaying.iocdemo;public class CtripBookingService implements BookingService {public void bookFlight() {System.out.println("book fight by Ctrip!");} }好了,土豪出門談生意,得訂機票了,Boss就琢磨著怎么訂票呢,Boss比較了一下價格,這一次決定用去哪兒,對應的Boss的代碼:
Boss.java
package com.tianmaying.iocdemo;public class Boss {private BookingService bookingService;public Boss() {this.bookingService = new QunarBookingService();}public BookingService getBookingService() {return bookingService;}public void setBookingService(BookingService bookingService) {this.bookingService = bookingService;}public void goSomewhere() {bookingService.bookFlight();} }在Boss的構造函數中,將其bookingService成員變量實例化為?QunarBookingService,goSomewhere()函數中就可以調用bookingService的bookFlight方法了!
為了把這個場景Run起來,我們還需要一個main函數:
package com.tianmaying.iocdemo;public class App {public static void main(String[] args) {bossGoSomewhere();}static void bossGoSomewhere() {Boss boss = new Boss();boss.goSomewhere();} }運行之后可以看到控制中可以打印出"book fight by Qunar!"了。
使用IoC的場景
?
在這個例子中,我們看到Boss需要使用BookingService,于是Boss自己實例化了一個QunarBookingService對象。同志們想想,身為土豪Boss,思考的都是公司戰略的事兒,定個票還要自己選擇通過什么方式來完成,這個Boss是不是當得實在太苦逼。
?
所以土豪趕緊給自己找了個美女秘書(別想歪!),Boss要出差時,只需要說一聲他需要訂票服務,至于是哪個服務,讓美女秘書選好后告訴他即可(注入啊!注入!)。(別跟我較真說美女秘書直接把票送上就行!)
?
這樣的話,Boss是不是一身輕松了? 而這個美女秘書還是免費包郵的,這正是Spring扮演的角色!來看看使用Spring之后的代碼。
?
我們在pom.xml文件中加入依賴(項目使用Maven作為構建工具):
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.2.0.RELEASE</version> </dependency>QunarBookingService.java
package com.tianmaying.iocdemo; import org.springframework.stereotype.Component;@Component public class QunarBookingService implements BookingService {public void bookFlight() {System.out.println("book fight by Qunar!");} }這里我們使用Spring的@Component標注將QunarBookingService注冊進Spring的Context,這樣它就可以被注入到需要它的地方!相應地,創建QunarBookingService實例的責任也交給了Spring。我們說了,美女秘書幫你搞定嘛!
?
新建一個SmartBoss類,聰明的老板知道把選擇訂機票服務這樣的雜事交給秘書來做。
SmartBoss.java
package com.tianmaying.iocdemo;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class SmartBoss {private BookingService bookingService;@Autowiredpublic void setBookingService(BookingService bookingService) {this.bookingService = bookingService;}public BookingService getBookingService() {return bookingService;}public void goSomewhere() {bookingService.bookFlight();} }在上面的代碼中,SmartBoss不再自己創建BookingService的實例,只是通過@Autowired標注告訴Spring小秘我需要一個BookingService!
?
調用代碼因此也要做一些小修改,需要創建Spring的Context:
static void smartBossGoSomewhere() {AbstractApplicationContext context = new AnnotationConfigApplicationContext(App.class);try {SmartBoss boss = context.getBean(SmartBoss.class);boss.goSomewhere();} finally {context.close();} }
IoC的好處
?
回到正題,通過上面的例子,我們來看看IoC到底帶來了哪些好處?
?
Boss沒有和某個具體的BookingService類耦合到一起了,這樣Boss的維護和演化就更加方便。想象一下,如果Boss需要改用CtripBookingService,這時也不需要修改Boss.java的代碼,更換接口的實現非常方便,給Boss注入新的實現即可,輕松愜意。(當然,要做到熱插拔還需要進一步的工作,要么得玩轉類加載器這玩意,或者借助OSGi這樣的神器)。這也是典型的開放-封閉原則的例子,即對現有模塊,功能擴展應該是開放的,而對其代碼修改應該是封閉的,即能夠做到不需要修改已有代碼來擴展新的功能。
?
想象一下,如果Boss自己直接去實例化QunarBookingService,而QunarBookingService在另外一個Package中甚至另外一個Jar包中,你可得import進來才能使用,緊耦合啊!現在好了,Boss只依賴于抽象接口,測試更方便了吧,Mock一下就輕松搞定!Boss和QunarBookingService彼此不知道對方,Spring幫兩者粘合在一起。
?
為什么IoC是個大招,因為它會自然而然得促進你應用一些好的設計原則,會幫助你開發出更加“高內聚低耦合”的軟件。
IoC如何實現
?
最后我們簡單說說IoC是如何實現的。想象一下如果我們自己來實現這個依賴注入的功能,我們怎么來做? 無外乎:
我們發現其實自己來實現也不是很難,Spring實際也就是這么做的。這么看的話其實IoC就是一個工廠模式的升級版!當然要做一個成熟的IoC框架,還是非常多細致的工作要做,Spring不僅提供了一個已經成為業界標準的Java IoC框架,還提供了更多強大的功能,所以大家就別去造輪子啦!希望了解IoC更多實現細節不妨通過學習Spring的源碼來加深理解!
?
例子中的源碼戳 Spring的IoC原理
?
針對評論的更新(2016-09-08):
Xeric的評論:DI不等于IoC,混為一談會干擾理解。IoC是方法論,DI是實現形式。Inversion of Control Containers and the Dependency Injection pattern
?
難得在知乎上有一個注重概念的,不錯!那就來說說。
什么是方法論,最接近的英文是Methodology,簡言之是概念體系+符號表示+過程指導,IoC是不是呢?顯然不是。關于IoC,其實你該引的文章是這篇InversionOfControl,是一種框架的現象(phenomenon),在你引用的文章里稱之為框架的特征(characteristic)。
Flowler大叔最早對IoC感到困惑(那時Rod Johnson已經一戰成名),為什么呢?如果對框架(軟件工程中的framework)有研究的人,都知道有個微內核的概念,更通俗一點地理解是GoF設計模式中也提到過的,你不要調用我,我來調用你,反轉嘛!這都多少年前的概念了,所以你一個容器說自己支持IoC,TM的不是類似說我的汽車有輪子碼? 這是一個再通用不過的框架特征罷了,任何一種開發平臺(iOS,Windows,Android),任何一個開發框架,不都控制反轉嘛。
注意你引的文章:
As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.
關鍵來了:所以在當時Spring和PicoContainer等輕量級容器的這個Context下,IoC這個概念真正表達的含義其實應該用一個更特化的概念來表達,這就是依賴注入了,這不叫“混為一談”。但是IoC大家還是這么著被大家用下來了,表達了一個相比它原來更加狹義的概念(軟件工程史上很多概念都是這樣,即使學術界也必然會被所謂“事實上的工業標準”所影響),因為那正是工業界框架橫飛迅猛發展的年代啊,參考Web 建站技術中,HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、ASP.NET、Web Services 是什么? - David 的回答。
至于什么“方法論”-“實現”這個層次,一些中文文章(比如百度百科)有見類似說法,大抵因為Flower說的:
I'll point out now that that's not the only way of removing the dependency from the application class to the plugin implementation.
注意啊注意,是有其他方式(比你你可能會說依賴查找),這個時候IoC的概念指的是removing the dependency from the application class to the plugin implementation,這又回到泛化的概念了。不是這些流行Container語境下的IoC。但是人云亦云,大家就都這么說了。
所以題主問的是Spring的IoC,文中也是介紹Spring,沒有任何不妥,說個Spring IoC的好處犯不著說方法論,就這個問題而言反而可能讓題主困惑。
歡迎探討。
總結
- 上一篇: 依赖注入IOC
- 下一篇: Spring松耦合的个人理解和代码实例