聲明:我已委託「維權騎士」(rightknights.com)爲個人文章進行維權行動。html
控制反轉(Inversion of Control, IoC)最先由Michael Mattsson在《Object-Oriented Frameworks:A survey of methodological issues》一文中提出。java
Wikipedia對於IoC的定義以下:web
In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of control as compared to traditional control flow. In IoC, custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.spring
在Martin Flow的《Inversion of Control Containers and the Dependency Injection pattern》中提到,在早期的終端程序時代,用戶界面主要是由程序自己控制,你的程序裏有不少命令,當程序提醒用戶輸入名字後並在收到用戶輸入的名字後,會作出相關的響應處理;而在圖形界面時代,UI Framework一般會包括一個主循環(Main Loop),你的編碼工做只須要提供事件處理器(Event Handler),來處理對應事件下的業務邏輯便可,不須要關係事件如何參數、什麼時候到來。若是將上述邏輯寫成代碼以下:express
在終端模式的業務代碼apache
public class Terminal {
public static void main(String[] args) {
while (true) {
Command command = obtainCommonForInput();
if (isInputNameCommand(command)) {
// 編寫收到姓名的業務邏輯
handleNameInputed();
} else {
// todo 添加其餘的邏輯,須要添加command類型判斷並編寫對應回調邏輯
}
}
}
}
複製代碼
在GUI模式的業務代碼編程
public class YourBizListener extends OnButtonClickListener {
public void onButtonClicked() {
// 編寫按鈕按下的業務邏輯
}
}
public class GUI {
public static void main(String[] args) {
Application application = createApplication();
registerListener(application, new YourBizListener());
// 若是須要響應其餘事件,只須要實現對應的事件處理接口,並註冊
}
}
複製代碼
上述兩端代碼的程序控制流關係和源碼依賴關係以下:設計模式
從程序控制流依賴和源碼依賴的角度看,在Terminal中,程序控制流的方向和源碼依賴方向保持相同,都是自上而下的;這意味着當你須要編寫函數main()
時,對應的函數必須已聲明(源碼依賴),這樣你才能調用相關代碼;編寫main函數的過程,就是肯定控制流依賴的過程,也是產生源碼依賴的方向。總之,代碼編寫者肯定程序的具體執行過程。可是,GUI中的狀況有所改變,圖中的源碼依賴仍是自上而下的,可是在事件響應的回調上,控制流的方向則自下而上的,和源碼依賴方向相反。 站在Framework的角度上看,源碼依賴的方式和控制流依賴的方向是相反的,這樣使得Framework能夠支持不一樣開發人員的使用場景。所以,咱們也將IoC稱之爲好萊塢原則:不要給咱們打電話,咱們會給你打電話(don't call us, we'll call you)。api
從關注點分離(Separation of concerns, SoC)角度看,IoC解決的問題是如何解耦when-to-do和what-to-do。 Framework經過抽象事件響應的執行過程,將事件響應從具體函數的調用轉化抽象接口OnButtonClickListener
的調用,解耦了事件響應執行(when-to-do )和事件響應實現(what-to-do)的關係;在此基礎上,開發人員實現接口OnButtonClickListener
並向Framework註冊,以達到響應相關按鈕的點擊事件的目的。從中能夠看出,決定事件回調是否執行的關鍵代碼並不禁開發人員掌握,而由Framework決定,代碼執行的決定權從開發人員手中轉移到Framework中,和以前相比方向上出現了反轉。tomcat
這裏說的「代碼執行的決定權」是指「代碼執行的直接決定權」。
例如,Framework直接決定當用戶點擊某個按鈕的時候,調用用戶定義的哪一個回調函數。可是,Framework仍是能夠經過對外提供配置修改接口讓開發人員間接干預Framework的具體執行過程。例如,在GUI的例子中,若是用戶禁用某個按鈕,Framework將不會響應該按鈕的點擊事件。
從總體上看,IoC主要分爲如下幾步:
關於what-to-do邏輯註冊的動做,不必定是有開發人員完成的。例如,Spring Bean容器中基於包名ComponentScan的bean發現註冊機制就是由開發人員和Spring Framework共同完成的;再例如Tomcat webapp的自動部署是由Tomcat自身實現的。
IoC的實現方式有不少,最爲常見的能夠分爲如下幾種:
簡單講,DI描述的問題是客戶端獲取一個對象的時候,Framework自動將該對象全部依賴的對象進行關聯,確保客戶端得到到的對象能夠直接使用,無需再繼續額外配置。 在沒有DI技術的方式下,用戶每建立一個對象,都須要對其相關屬性進行單獨的配置。
一般,DI分爲構造器注入、字段注入、接口注入等三種注入方式。他們的使用場景也會存在很大的不一樣:
模板方法模式 & 策略模式是設計模式中IoC的典範。在實現上,他們都利用面向對象編程的多態特性,實現when to do和 what to do的關注點分離。 在模板方法模式中,抽象類決定 what-to-do協議約定,只關注when to do,對應具體類實現what to do,when to do 和what to do 的關係綁定是由類繼承關係肯定的;策略模式下,策略接口約定do的具體內容,Context環境類肯定when to do,提供what to do 策略的管理,對應的策略實現類決定what to do的具體邏輯。
服務定位(ServiceLoader)利用spi技術用於特定服務組件的加載,能夠達到組件插件化的設計目的。