一直想用心寫這個系列的文章,其實看得越多,也就越以爲本身在這方面的功力太淺,也就越不想班門弄斧啦,做爲一個開篇,我想把這個技術深層次化,在以前的.net的一個MVC系列文章其實已經涉及到了,只是.net在這方面的應用不如java來得這麼猛烈,這麼酣汗淋漓,因此也就下定決心,這一階段針對這一個技術點進行深層次的剖析。html
IOC,英文全稱Inversion of Control,中文直譯控制反轉,通常咱們稱之爲「依賴注入」原則,在我還未過多涉足java領域時,在C#語言24個設計模式中(參見《大話設計模式》)有一個依賴倒轉原則,做爲一個設計模式的原則而不是一個模式。在接觸java以後,尤爲是spring框架後,被稱之爲「依賴注入」IOC設計模式。其實大致的意思和意義都是差很少的,只是在我看來,在C#領域對這個技術的應用沒有那麼普遍,限於.NET技術的先天性,我不多看到這方面的巨做,也許是我太膚淺。在我上次寫基於.NET的MVC ProDinner系列文章時候,查找這方面的文章,幾乎是爲零,只能本身去看MVC源碼或者Castle 項目源碼。java
首先看看《大話設計模式》中基於C#語言如何詮釋這門藝術的。原著由維修計算機入題,在咱們平常生活中,咱們很天然地,提升計算機速度須要更高的CPU,須要更快的系統響應度就須要更好的內存,須要更大的存儲介質,就須要擴大硬盤,在這一系列的活動中,因而咱們就天然而然的發現,計算機速度依賴CPU,在CPU的使用中咱們歷來不關心CPU是怎麼設計出來的,咱們只須要關注這是Intel仍是AMD的CPU,由於他決定了你的主板類型,當肯定了CPU以後,咱們只需關注你須要使用那款CPU,由於接口主板都是同樣的,也便是說,大的功能不會去關注具體每一款產品的實現,而只須要關注產品與產品之間的標準化接口。由此引出來這個原則的定義:程序員
A、高層模塊不該該依賴低層模塊,兩個都應該依賴抽象spring
B、抽象不該該依賴細節,細節應該依賴抽象數據庫
這麼講實際上是比較抽象的,具體到代碼中實際上就是,層於層之間,各模塊間,各項目間都應該使用接口層來完成模塊的融合。 當某個角色(多是一個對象實例,調用者)須要另外一個角色(另外一個對象實例,被調用者)的協助時,在傳統的程序設計過程當中,一般由調用者來建立被調用者的實例。而控制反轉中建立被調用者的工做再也不由調用者來完成。建立被調用者 實例的工做一般由特定的容器來完成,而後注入調用者,所以也稱爲依賴注入。設計模式
廢話很少說,理論講得再精彩最終看代碼實現,依據我對這些該概念的理解,咱們一塊兒看基於C#語言寫出的實例:app
需求場景:框架
父親在完成一件事情的時候,因爲各方面緣由須要本身的子女幫忙才能完成,因此,父親須要去調動本身的兒子或者女兒才能完成這件事情eclipse
類型設計:函數
接口 Iperson爲被調用者的基類型
類 Father 調用者
類 Son 被調用者
類 Daughter 被調用者
類 Container 依賴注入容器
程序分析:
Father類經過調用抽象層接口,來完成這個操做,至於調用兒子仍是女兒咱們在構造函數中完成類型的選擇。也就是說Father是調用者,Son和Daughter是被調用者,Container是依賴注入的生成容器。負責將調用者Son或者Daughter對象實例化給調用者。
.NET 程序目錄結構:
打開visual studio 創建控制檯應用程序,接口層代碼實現:
1 //接口層 2 public interface Iperson 3 { 4 void operation(); 5 }
調用者代碼實現:
1 //調用者實現類 2 public class Father 3 { 4 Iperson iperson; 5 6 Container container = new Container(); 7 8 public Father(String typeName) 9 { 10 iperson = container.GetApplication(typeName); 11 } 12 public void operation() 13 { 14 iperson.operation(); 15 } 16 }
Container 在這裏我直接new出實例了,看到這個代碼熟悉java spring框架的同窗就似曾相識了,通常是用@autowired來修飾依賴注入的操做句柄。Father類中的operation方法並無本身去實現任何操做,而是調用了iperson的操做來實現本身的方法,而iperson只是一個接口類型,須要具體的實現類,代碼才能運行起來,咱們看iperson的兩個實現層:
//被調用者類 ipseron實現層 public class Son:Iperson { public void operation() { Console.WriteLine("son is the operator"); } } //被調用者之二 public class Daughter:Iperson { public void operation() { Console.WriteLine("daughter is the operator"); } }
Conatiner是實現容器,原理其實很簡單,反射技術,反射原理進行不一樣的封裝其實就造成了不一樣的技術框架,java spring的ioc核心離不開反射,許多的數據庫mapper框架一樣也離不開反射,看看代碼實現:
//生成實例容器 public class Container { public Iperson GetApplication(String typeName) { return (Iperson) Activator.CreateInstance(Type.GetType(typeName)); } }
固然,我這是最基本的反射實現了,在生成化產品化的框架中反射遠沒有這麼簡單,但原理都是相同的。好,看下測試類實現代碼:
class Program { static void Main(string[] args) { //調用son Father fa = new Father("IOCblog.Son"); fa.operation(); //調用daughter fa = new Father("IOCblog.Daughter"); fa.operation(); } }
程序運行結果:
代碼寫到這裏對這個概念有所掌握的同窗,其實會是有共鳴的,這是依賴注入最基本的實現了,平常項目開發中基於框架級別來實現這種模式思想,咱們不少時候是用修飾符或者配置文件,典型的就是java下面的spring的實現。java spring框架可沒那麼簡單,spring兩大核心ioc和aop,實現的手段無非也是如此,但不一樣的是思想!思想!我這裏如若再用java套spring框架來作示例實現java下的IOC的思想,確定也就沒人有興趣繼續看下去了,這篇文章的含金量就沒那麼誘人啦,那麼咱們來高級點的:
基於java代碼的模擬spring框架IOC實現
實現java下的ioc毫無疑問,咱們首先專業術語就換成了,咱們須要JavaBean來實現注入,須要factory來實現容器,實現步奏:
一、創建須要實現注入的JavaBean,注入類Person和容器類Persons
二、創建相似spring框架的application.xml配置文件,將依賴的JavaBean和相應注入的屬性配置在xml文件中,咱們這裏取名叫 IocApplication.xml
三、實現注入容器的factory類,主要負責讀配置文件->依據配置文件進行對象實例化->放入persons map集合當中,以備調用。
java程序目錄結構:
大致實現思路如上所述,好,廢話很少說,看看代碼如何實現,第一步和第二步代碼以下(切換到eclipse下,創建java應用程序):
package com.ioc.bean; //被調用注入類 public class Person { private String name; public String getName() { return this.name; } public void setName(String name) { this.name = name; } }
package com.ioc.bean; //beans類 public class Persons { private Person son; public Person getSon() { return this.son; } public void setSon(Person son) { this.son = son; } private Person daughter; public Person getDaughter() { return this.daughter; } public void setDaughter(Person daughter) { this.daughter = daughter; } }
實現的容器factory代碼量比較大,這裏就不依依貼出,全部的源代碼都在附件中可自由下載,這裏主要分析幾個關鍵步奏:
實現容器,主要負責一件事件,經過配置的xml,對相應的javabean進行反射,生成實例化對象,存入map中,xml配置實現:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="son" class="com.ioc.bean.Person"> <property name="name"> <value>mark</value> </property> </bean> <bean id="daughter" class="com.ioc.bean.Person"> <property name="name"> <value>cindy</value> </property> </bean> <bean id="father" class="com.ioc.bean.Persons"> <property name="son"> <ref bean="son" /> </property> <property name="daughter"> <ref bean="daughter" /> </property> </bean> </beans>
容器實現map操做代碼:
private Map<String,Object> beanMap=new HashMap<String,Object>(); /** * 初始化xml文件 * */ public void init(String xmlUrl){ SAXReader saxReader=new SAXReader(); File file=new File(xmlUrl); try{ saxReader.addHandler("/beans/bean",new BeanHandler()); saxReader.read(file); } catch(DocumentException e){ System.out.println(e.getMessage()); } } /** * 根據beanid來獲取bean * */ public Object getBean(String beanId){ Object obj=null; obj=beanMap.get(beanId); return obj; }
BeanHandler毫無疑問就是具體去操做IocApplication.xml文件的操做類,代碼比較長,能夠下載附件源代碼查看。
好,看下測試類代碼:
package com.ioc.test; import com.ioc.bean.Person; import com.ioc.factory.BeanFactory; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub String xmlUrl="src/IocApplication.xml"; BeanFactory factory=new BeanFactory(); factory.init(xmlUrl); Person me=(Person)factory.getBean("son"); System.out.println("Son's name:"+me.getName()); } }
程序運行結果:
java 模擬spring實現,能夠參考這篇文章:http://only1.iteye.com/blog/733550
結束語:
這篇文章咱們暫且不來比較依賴注入和反轉的區別,依賴反轉和依賴注入不少程序員把這兩個概念當成一個理解,認爲只是不一樣的解釋,其實細分仍是有區別的
一、依賴注入是從應用程序的角度在描述,也就是說,應用程序依賴容器建立並注入它所須要的外部資源;而控制反轉是從容器的角度在描述,容器控制應用程序,由容器反向的嚮應用程序注入應用程序所須要的外部資源。
二、IOC,控制反轉是軟件運行時體現出來的一個特徵:若是對象A運行時依賴於對象B,但A並不去建立B,而是從外界直接取得B。也就是說,一個對象並非本身去建立它所依賴的其它對象。DI,依賴注入是控制反轉的一種實現手段。如上面的例子,B的取得並不須要A的干涉,而是利用某些框架在經過構造參數或屬性設置來實現。
關於這些這裏就不展開敘述,後面咱們再詳細討論,這節主要討論依賴注入,咱們首先在這節暫時認爲這是一個相同的概念。做爲概述,首先咱們瞭解下IOC概念,再看看java和C#在這一律念上的運用比較。
寫一篇博文基本是一個禮拜的積累,雕琢,不容易,無私的奉獻轉載註明出處,http://www.cnblogs.com/aspnetdream/p/4035677.html。
最後附上個人源代碼
Java代碼 http://files.cnblogs.com/aspnetdream/iocblog.rar
C#代碼 http://files.cnblogs.com/aspnetdream/Csharp_IOCblog.rar