在學習Spring的時候,意外找到這個控制反轉(IoC)和麪向切面(AOP)的容器框架以前,咱們先來看一下什麼是控制反轉(IoC)。java
控制反轉(Ioc)和依賴注入(DI)剛聽到感到很難理解,平時也程序也不多想到這一點,這幾天學Spring的相關資料是看到的最多的是這個概念,網上放狗搜了一下,內容挺多。總算明白了一些。算法
Ioc,照個人理解應該是爲了知足高內聚低耦合的設計原則,將對象的建立和獲取交給外部容器來控制,從外部容器的角度(第三方參照物)來看,達到程序控制權的轉移,這樣估計好理解了。spring
DI,依賴注入,從字面的意思來講是從外部導入的方式實現低耦合,好比構造函數、屬性設置等。設計模式
控制反轉(Inversion of Control,英文縮寫爲IoC),也叫依賴注入(Dependency Injection)。我我的認爲控制反轉的意思是依賴對象發生改變,由最初的類自己來管理依賴對象改變爲IoC框架來管理這些對象,使得依賴脫離類自己的控制,從而實現鬆耦合。
咱們先來看一段代碼服務器
namespace Dao { public interface IPersonDao { void Save(); } public class PersonDao : IPersonDao { public void Save() { Console.WriteLine("保存 Person"); } } } namespace SpringNetIoC { class Program { private static void NormalMethod() { IPersonDao dao = new PersonDao(); dao.Save(); Console.WriteLine("我是通常方法"); } } }
namespace DaoFactory { public static class DataAccess { public static IPersonDao CreatePersonDao() { return new PersonDao(); } } }
namespace SpringNetIoC { class Program { private static void FactoryMethod() { IPersonDao dao = DataAccess.CreatePersonDao(); dao.Save(); Console.WriteLine("我是工廠方法"); } } }
App.config <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> </sectionGroup> </configSections> <spring> <context> <resource uri="config://spring/objects" /> </context> <objects xmlns="http://www.springframework.net"> <description>一個簡單的控制反轉例子</description> <object id="PersonDao" type="Dao.PersonDao, Dao" /> </objects> </spring> </configuration>
這樣從必定程度上解決了Program與PersonDao耦合的問題,可是實際上並無徹底解決耦合,只是把耦合放到了XML 文件中,經過一個容器在須要的時候把這個依賴關係造成,即把須要的接口實現注入到須要它的類中。我我的認爲能夠把IoC模式看作是工廠模式的昇華,能夠把 IoC看做是一個大工廠,只不過這個大工廠裏要生成的對象都是在XML文件中給出定義的。
依賴注入
1.
控制反轉
(Inversion of Control)
與依賴注入
(Dependency Injection)
控制反轉即IoC (Inversion of Control),它把傳統上由程序代碼直接操控的對象的調用權交給容器,經過容器來實現對象組件的裝配和管理。所謂的「控制反轉」概念就是對組件對象控制權的轉移,從程序代碼自己轉移到了外部容器。
IoC是一個很大的概念,能夠用不一樣的方式來實現。其主要實現方式有兩種:<1>依賴查找(Dependency Lookup):
容器提供回調接口和上下文環境給
組件。EJB和Apache Avalon都使用這種方式。<2>依賴注入(Dependency Injection):組件不作定位查詢,只提供普通的Java方法讓容器去決定依賴關係。後者是時下最流行的IoC類型,其又有接口注入(Interface Injection),設值注入(Setter Injection)和構造子注入(Constructor Injection)三種方式。
圖1 控制反轉概念結構
依賴注入之因此更流行是由於它是一種更可取的方式:讓容器全權負責依賴查詢,受管組件只須要暴露JavaBean的setter方法或者帶參數的構造子或者接口,使容器能夠在初始化時組裝
對象的依賴關係。其與依賴查找方式相比,主要優點爲:<1>查找定位操做與應用代碼徹底無關。<2>不依賴於容器的API,能夠很容易地在任何容器之外使用應用對象。<3>不須要特殊的接口,絕大多數對象能夠作到徹底沒必要依賴容器。
2.
好萊塢原則
IoC體現了好萊塢原則,即「不要打電話過來,咱們會打給你」。第一次遇到好萊塢原則是在瞭解模板方法(Template Mathod)模式的時候,模板方法模式的核心是,基類(抽象類)定義了算法的骨架,而將一些步驟延遲到子類中。
圖2 模板方法模式類圖
如今來考慮IoC的實現機制,組件定義了整個流程框架,而其中的一些業務邏輯的實現要藉助於其餘業務對象的加入,它們能夠經過兩種方式參與到業務流程中,一種是依賴查找(Dependency Lookup),相似與JDNI的實現,經過JNDI來找到相應的業務對象(代碼1),另外一種是依賴注入,經過IoC容器將業務對象注入到組件中。
3.
依賴查找(
Dependency Lookup
)
下面代碼展現了基於JNDI實現的依賴查找機制。
代碼1依賴查找(Dependency Lookup)代碼實現
依賴查找的主要問題是,這段代碼必須依賴於JNDI環境,因此它不能在應用服務器以外運行,而且若是要用別的方式取代JNDI來查找資源和協做對象,就必須把JNDI代碼抽出來重構到一個策略方法中去。
4.
依賴注入(
Dependency Injection
)
依賴注入的基本原則是:應用組件不該該負責查找資源或者其餘依賴的協做對象。配置對象的工做應該由IoC容器負責,「查找資源」的邏輯應該從應用組件的代碼中抽取出來,交給IoC容器負責。
下面分別演示3中注入機制。
代碼2 待注入的業務對象Content.java
package com.zj.ioc.di; public class Content { public void BusniessContent(){ System.out.println("do business"); } public void AnotherBusniessContent(){ System.out.println("do another business"); } }
MyBusniess類展現了一個業務組件,它的實現須要對象Content的注入。代碼3,代碼4,代碼5,6分別演示構造子注入(Constructor Injection),設值注入(Setter Injection)和接口注入(Interface Injection)三種方式。
代碼3構造子注入(Constructor Injection)MyBusiness.java
package com.zj.ioc.di.ctor; import com.zj.ioc.di.Content; public class MyBusiness { private Content myContent; public MyBusiness(Content content) { myContent = content; } public void doBusiness(){ myContent.BusniessContent(); } public void doAnotherBusiness(){ myContent.AnotherBusniessContent(); } }
代碼4設值注入(Setter Injection) MyBusiness.java
package com.zj.ioc.di.set; import com.zj.ioc.di.Content; public class MyBusiness { private Content myContent; public void setContent(Content content) { myContent = content; } public void doBusiness(){ myContent.BusniessContent(); } public void doAnotherBusiness(){ myContent.AnotherBusniessContent(); } }
代碼5 設置注入接口InContent.java
package com.zj.ioc.di.iface; import com.zj.ioc.di.Content; public interface InContent { void createContent(Content content); }
代碼6接口注入(Interface Injection)MyBusiness.java
package com.zj.ioc.di.iface; import com.zj.ioc.di.Content; public class MyBusiness implements InContent{ private Content myContent; public void createContent(Content content) { myContent = content; } public void doBusniess(){ myContent.BusniessContent(); } public void doAnotherBusniess(){ myContent.AnotherBusniessContent(); } }
5.
依賴拖拽
(Dependency Pull)
最後須要介紹的是依賴拖拽,注入的對象如何與組件發生聯繫,這個過程就是經過依賴拖拽實現。
代碼7 依賴拖拽示例
public static void main(String[] args) throws Exception{ //get the bean factory BeanFactory factory = getBeanFactory(); MessageRender mr = (MessageRender) factory.getBean(「renderer」); mr.render(); }
而一般對注入對象的配置能夠經過一個xml文件完成。
使用這種方式對對象進行集中管理,使用依賴拖拽與依賴查找本質的區別是,依賴查找是在業務組件代碼中進行的,而不是從一個集中的註冊處,特定的地點執行。
|