有部分?Java?開發(fā)者對 IoC(Inversion Of Control)和 DI(Dependency Injection)的概念有些混淆,認(rèn)為二者是對等的。
IoC 其實(shí)有兩種方式,一種就是 DI,而另一種是 DL,即 Dependency Lookup(依賴查找),前者是當(dāng)前軟件實(shí)體被動(dòng)接受其依賴的其他組件被 IoC 容器注入,而后者則是當(dāng)前軟件實(shí)體主動(dòng)去某個(gè)服務(wù)注冊地查找其依賴的那些服務(wù),概念之間的關(guān)系如圖 1 所示可能更貼切些。

圖 1??IoC相關(guān)概念示意圖
我們通常提到的?Spring?IoC,實(shí)際上是指 Spring 框架提供的 IoC 容器實(shí)現(xiàn)(IoC Container),而使用 Spring IoC 容器的一個(gè)典型代碼片段就是:
public class App {
? ? public static void main(String[] args) {
? ? ? ? ApplicationContext context = new FileSystemXmlApplication-Context("...");
? ? ? ? // ...
? ? ? ? MockService service = context.getBean(MockService.class);
? ? ? ? service.doSomething();
? ? }
}
任何一個(gè)使用 Spring 框架構(gòu)建的獨(dú)立的 Java 應(yīng)用(Standalone Java Application),通常都會(huì)存在一行類似于“context.getBean(..);”的代碼。
實(shí)際上,這行代碼做的就是 DL 的工作,而構(gòu)建的任何一種 IoC 容器背后(比如 BeanFactory 或者 ApplicationContext)發(fā)生的事情,則更多是 DI 的過程(也可能有部分 DL 的邏輯用于對接遺留系統(tǒng))。
Spring 的 IoC 容器中發(fā)生的事情其實(shí)也很簡單,總結(jié)下來即兩個(gè)階段:
采摘和收集“咖啡豆”(bean)
研磨和烹飪咖啡
Spring IoC 容器的依賴注入工作可以分為兩個(gè)階段:
1)收集和注冊
第一個(gè)階段可以認(rèn)為是構(gòu)建和收集 bean 定義的階段,在這個(gè)階段中,我們可以通過 XML 或者 Java 代碼的方式定義一些 bean,然后通過手動(dòng)組裝或者讓容器基于某些機(jī)制自動(dòng)掃描的形式,將這些 bean 定義收集到 IoC 容器中。
假設(shè)我們以 XML 配置的形式來收集并注冊單一 bean,一般形式如下:
<bean id="mockService" class="..MockServiceImpl"> ...</bean>
如果嫌逐個(gè)收集 bean 定義麻煩,想批量地收集并注冊到 IoC 容器中,我們也可以通過 XML Schema 形式的配置進(jìn)行批量掃描并采集和注冊:
<context:component-scan base-package="com.keevol">
注意基于 JavaConfig 形式的收集和注冊,不管是單一還是批量,后面我們都會(huì)單獨(dú)提及。
2)分析和組裝
當(dāng)?shù)谝浑A段工作完成后,我們可以先暫且認(rèn)為 IoC 容器中充斥著一個(gè)個(gè)獨(dú)立的 bean,它們之間沒有任何關(guān)系。
但實(shí)際上,它們之間是有依賴關(guān)系的,所以,IoC 容器在第二階段要干的事情就是分析這些已經(jīng)在 IoC 容器之中的 bean,然后根據(jù)它們之間的依賴關(guān)系先后組裝它們。
如果 IoC 容器發(fā)現(xiàn)某個(gè) bean 依賴另一個(gè) bean,它就會(huì)將這另一個(gè) bean 注入給依賴它的那個(gè) bean,直到所有 bean 的依賴都注入完成,所有 bean 都“整裝待發(fā)”,整個(gè) IoC 容器的工作即算完成。
至于分析和組裝的依據(jù),Spring 框架最早是通過 XML 配置文件的形式來描述 bean 與 bean 之間的關(guān)系的,隨著 Java 業(yè)界研發(fā)技術(shù)和理念的轉(zhuǎn)變,基于 Java 代碼和 Annotation 元信息的描述方式也日漸興盛(比如 @Autowired 和 @Inject),但不管使用哪種方式,都只是為了簡化綁定邏輯描述的各種“表象”,最終都是為本階段的最終目的服務(wù)。
很多 Java 開發(fā)者一定認(rèn)為 Spring 的 XML 配置文件是一種配置(Configuration),但本質(zhì)上,這些配置文件更應(yīng)該是一種代碼形式,XML 在這里其實(shí)可以看作一種 DSL,它用來表述的是 bean 與 bean 之間的依賴綁定關(guān)系,如果沒有 IoC 容器就要自己寫代碼新建(new)對象并配置(set)依賴。