一、Spring的IoC(Inversion of Control)。
這是Spring中得有特點的一部份。IoC又被翻譯成“控制反轉”,也不知道是誰翻譯得這么別扭,感覺很深奧的詞。其實,原理很簡單,用一句通俗的話來說:就是用XML來定義生成的對象。IoC其實是一種設計模式,Spring只是實現了這種設計模式。
這種設計模式是怎么來的呢?是實踐中逐漸形成的。
第一階段:用普通的無模式來寫Java程序。一般初學者都要經過這個階段。
第二階段:頻繁的開始使用接口,這時,接口一般都會伴隨着使用工廠模式。
第三階段:使用IoC模式。工廠模式還不夠好:(1)因為的類的生成代碼寫死在程序里,如果你要換一個子類,就要修改工廠方法。(2)一個接口常常意味着一個生成工廠,會多出很多工廠類。
可以把IoC模式看做是工廠模式的升華,可以把IoC看作是一個大工廠,只不過這個大工廠里要生成的對象都是在XML文件中給出定義的,然后利用Java的“反射”編程,根據XML中給出的類名生成相應的對象。從實現來看,IoC是把以前在工廠方法里寫死的對象生成代碼,改變為由XML文件來定義,也就是把工廠和對象生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。
IoC中最基本的Java技術就是“反射”編程。反射又是一個生澀的名詞,通俗的說反射就是根據給出的類名(字符串)來生成對象。這種編程方式可以讓對象在生成時才決定要生成哪一種對象。我在最近的一個項目也用到了反射,當時是給出一個.properties文本文件,里面寫了一些全類名(包名+類名),然后,要根據這些全類名在程序中生成它們的對象。反射的應用是很廣泛的,象Hibernate、String中都是用“反射”做為最基本的技術手段。
在過去,反射編程方式相對於正常的對象生成方式要慢10幾倍,這也許也是當時為什么反射技術沒有普通應用開來的原因。但經SUN改良優化后,反射方式生成對象和通常對象生成方式,速度已經相差不大了(但依然有一倍以上的差距)。
所以要理解IoC,你必須先了解工廠模式和反射編程,否則對它產生的前因后果和實現原理都是無法理解透徹的。只要你理解了這一點,你自己也完全可以自己在程序中實現一個IoC框架,只不是這還要涉及到XML解析等其他知識,稍微麻煩一些。
IoC最大的好處是什么?因為把對象生成放在了XML里定義,所以當我們需要換一個實現子類將會變成很簡單(一般這樣的對象都是現實於某種接口的),只要修改XML就可以了,這樣我們甚至可以實現對象的熱插撥(有點象USB接口和SCIS硬盤了)。
IoC最大的缺點是什么?(1)生成一個對象的步驟變復雜了(其實上操作上還是挺簡單的),對於不習慣這種方式的人,會覺得有些別扭和不直觀。(2)對象生成因為是使用反射編程,在效率上有些損耗。但相對於IoC提高的維護性和靈活性來說,這點損耗是微不足道的,除非某對象的生成對效率要求特別高。(3)缺少IDE重構操作的支持,如果在Eclipse要對類改名,那么你還需要去XML文件里手工去改了,這似乎是所有XML方式的缺憾所在。
總的來說IoC無論原理和實現都還算是很簡單的。一些人曾認為IoC沒什么實際作用,這種說法是可以理解的,因為如果你在編程中很少使用接口,或很少使用工廠模式,那么你根本就沒有使用IoC的強烈需要,也不會體會到IoC可貴之處。有些人也說要消除工廠模式、單例模式,但是都語焉不詳、人雲亦雲。但如果你看到IoC模式和用上Spring,那么工廠模式和單例模式的確基本上可以不用了。但它消失了嗎?沒有!Spring的IoC實現本身就是一個大工廠,其中也包含了單例對象生成方式,只要用一個設置就可以讓對象生成由普通方式變單一實例方式,非常之簡單。
總結:
(1)IoC原理很簡單,作用的針對性也很強,不要把它看得很玄乎。
(2)要理解IoC,首先要了解“工廠、接口、反射”這些概念。
二、Spring中IOC的實現
了解了IOC模式的思想以及其優點,再來學習其實現。上面大致描述了PicoContainer以及Spring各自對IOC的實現,這篇來詳細看一下Spring中它的實現。
Spring中IOC貫穿了其整個框架,但正如martinflower所說:“saying that these lightweight containers are special because they use inversion of control is like saying my car is special because it has wheels”,IOC已經稱為框架設計中必不可少的部分。就實現上來講Spring采取了配置文件的形式來實現依賴的注射,並且支持Type2 IOC(Setter Injection)以及Type3 IOC(Constructor Injection)。
Spring中IOC的實現的核心是其Core Bean Factory,它將框架內部的組件以一定的耦合度組裝起來,並對使用它的應用提供一種面向服務的編程模式(SOP:Service-Orient Programming),比如Spring中的AOP、以及持久化(Hibernate、ibatics)的實現。
首先從最底層最基礎的factory Bean開始,先來看org.springframework.beans.factory.Bean
Factory接口,它是一個非常簡單的接口,getBean方法是其中最重要的方法,Spring通常是使用xml來populate Bean,所以比較常用的是XMLFactoryBean。
用一個簡單的示例看一下其用法。首先寫下兩個Bean類:
ExampleBean 類:

2
3 private String psnName=null;
4
5 private RefBean refbean=null;
6
7 private String addinfo=null;
8
9
10
11 public String getAddinfo() {
12
13 return getRefbean().getAddress()+getRefbean().getZipcode();
14
15 }
16
17 public String getPsnName() {
18
19 return psnName;
20
21 }
22
23 public void setPsnName(String psnName) {
24
25 this.psnName = psnName;
26
27 }
28
29 public void setRefbean(RefBean refbean) {
30
31 this.refbean = refbean;
32
33 }
34
35 public RefBean getRefbean() {
36
37 return refbean;
38
39 }
40
41 public void setAddinfo(String addinfo) {
42
43 this.addinfo = addinfo;
44
45 }
46
47 }
RefBean類:

2
3 public String getAddress() {
4
5 return address;
6
7 }
8
9 public void setAddress(String address) {
10
11 this.address = address;
12
13 }
14
15 public String getZipcode() {
16
17 return zipcode;
18
19 }
20
21 public void setZipcode(String zipcode) {
22
23 this.zipcode = zipcode;
24
25 }
26
27 private String zipcode=null;
28
29 private String address=null;
30
31 }
其xml配置文件 Bean.xml

2
3 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
4
5 "http://www.springframework.org/dtd/spring-beans.dtd">
6
7 <beans>
8
9 <bean id="exampleBean" class="test.ExampleBean">
10
11 <property name="psnName"><value>xkf</value></property>
12
13 <property name="refbean">
14
15 <ref bean="refBean"/>
16
17 </property>
18
19 </bean>
20
21 <bean id="refBean" class="test.RefBean">
22
23 <property name="address"><value>BeiJing</value></property>
24
25 <property name="zipcode"><value>100085</value></property>
26
27 </bean>
28
29 </beans>
然后可以寫個測試類來測試,當然,需要Spring中的Spring-core.jar以及commons-logging.jar,當然在elipse中可以通過安裝spring-ide插件來輕松實現。

2
3 public static void main(String[] args){
4
5 try{
6
7 Resource input = new ClassPathResource("test/Bean.xml");
8
9 System.out.println("resource is:"+input);
10
11 BeanFactory factory = new XmlBeanFactory(input);
12
13 ExampleBean eb =
14
15 (ExampleBean)factory.getBean("exampleBean");
16
17 System.out.println(eb.getPsnName());
18
19 System.out.println(eb.getAddinfo());
20
21 }
22
23 catch(Exception e){
24
25 e.printStackTrace();
26
27 }
28
29 }
30
這樣,通過BeanFactory的getBean方法,以及xml配置文件,避免了在test類中直接實例化ExampleBean,消除了應用程序(Test)與服務(ExampleBean)之間的耦合,實現了IOC(控制反轉)或者說實現了依賴的注射(Dependency Injection)。