控制反轉 vs 依賴注入
一、介紹
ioc(控制反轉)意味物件不用自己建立需要的物件,相反,它們直接從其他地方獲得需要的物件。DI(依賴注入)是一個IoC的具體實現,即在執行時通過不同的注入技術,如setter注入、constructor注入或介面注入來提供物件依賴。由於DI是IoC的具體實現,因此看起來兩則貌似一致,但是它們的側重點不同,IoC側重物件的建立過程,即物件建立建立的控制權交給了其他地方,而DI側重依賴的注入方式,即通過不同的技術動態注入依賴。
在spring中,ioc和DI經常會混為一談,因為,spring文件自己都說Ioc也被稱為DI,但是在spring中,Ioc更多的是用來使能DI的。
二、控制反轉
ioc可以這麼理解,其他人來幫你建立物件,而不是自己手動編碼“new MyObject”。這個其他人一般就是IOC容器,ioc容器來幫你建立物件,而ioc容器不只是建立物件,還會管理物件的宣告週期,每個不同的生命週期都會被ioc容器呼叫不同的方法。一些ioc容器,比如spring容器、java Servlets容器和Akka Actors。spring容器不僅負責bean的建立,還控制bean的生命週期。servlets容器也是負責建立servlet和管理他的生命週期。
儘管通過IOC,讓出了物件建立的控制權,但是還是需要提供模板來讓容器建立物件,如下面所示:
IoC Container | Managed Objects Name | Managed Objects Definition |
---|---|---|
Spring Container | Bean | Classes defined with annotations/XML configuration |
Servlet Container | Servlet | Classes implementing interface Servlet |
Actor System | Actor | Classes extending trait Actor |
三、依賴注入
簡單來說,DI和硬編碼注入是相對的:
//Hardcoded dependency public class MyClass { private MyDependency myDependency = new MyDependency(); }
//Injected dependency
public class MyClass {
private MyDependency myDependency;
public MyClass(MyDependency myDependency){
this.myDependency = myDependency;
}
}
可以看出,可以通過傳參給建構函式、setter方法來注入依賴。但是DI有個缺點,就是需要管理依賴和手動注入依賴,即建立依賴物件,然後傳參。
下面看一個例子,MyClass1依賴MyClass2,MyClass2依賴MyClass3:
public class MyClass3 {
public void doSomething(){}
}
//MyClass2 depends on MyClass3
public class MyClass2 {
private MyClass3 myClass3;
public MyClass2(MyClass3 myClass3){
this.myClass3 = myClass3;
}
public void doSomething(){
myClass3.doSomething();
}
}
//MyClass1 depends on MyClass2
public class MyClass1 {
private MyClass2 myClass2;
public MyClass1(MyClass2 myClass2){
this.myClass2 = myClass2;
}
public void doSomething(){
myClass2.doSomething();
}
}
public class Main {
public static void main(String[] args) {
//All dependencies need to be managed by the developer
MyClass3 myClass3 = new MyClass3();
MyClass2 myClass2 = new MyClass2(myClass3);
MyClass1 myClass1 = new MyClass1(myClass2);
myClass1.doSomething();
}
}
繼續下去,如果MyClass2還依賴MyClass4,那麼還需要手動新增新依賴:
public class MyClass2 {
private MyClass3 myClass3;
private MyClass4 myClass4;
public MyClass2(MyClass3 myClass3, MyClass4 myClass4){
this.myClass3 = myClass3;
this.myClass4 = myClass4;
}
public void doSomething(){
myClass3.doSomething();
myClass4.doSomething();
}
}
public class Main {
public static void main(String[] args) {
MyClass4 myClass4 = new MyClass4();
MyClass3 myClass3 = new MyClass3();
MyClass2 myClass2 = new MyClass2(myClass3, myClass4);
MyClass1 myClass1 = new MyClass1(myClass2);
myClass1.doSomething();
}
}
即使這樣描述並不是錯的,但是現實中的一個程式成百上千個依賴分散在不同的程式碼區域中,我們需要找到,然後再集中起來建立依賴和管理依賴,如上面的例子似的。很不方便,這就是接下來要討論的。
四、Ioc和DI配合
依賴太多,依賴的建立和依賴注入都很複雜。但是通過IOC,這些依賴可以被IOC容器來管理、建立。通過使用類似@Autowired的註解,容器可以自動在需要的地方注入依賴,因此程式設計師不需要關注依賴的建立和注入。
public class MyClass1 {
@Autowired
private MyClass2 myClass2;
public void doSomething(){
myClass2.doSomething();
}
}
public class MyClass2 {
@Autowired
private MyClass3 myClass3;
@Autowired
private MyClass4 myClass4;
public void doSomething(){
myClass3.doSomething();
myClass4.doSomething();
}
}
上面沒有了物件的建立過程,這是因為ioc容器被提供模板後,會自動建立依賴,比如spring用<bean>標籤定義模板。