理解spring (轉載)

wzdoxu88發表於2007-08-07
最近研究Spring,她包含的程式設計思想讓我耳目一新。所以寫下這篇入門級文章供新手參考。我不是什麼Spring的資深研究人員,我只是現學現賣。所以文章也只能是膚淺單薄,錯誤難免,還請見諒。
一、 Spring誕生
Spring是一個開源框架,目前在開源社群的人氣很旺,被認為是最有前途的開源框架之一。她是由Rod Johnson建立的,她的誕生是為了簡化企業級系統的開發。說道Spring就不得不說EJB,因為Spring在某種意義上是EJB的替代品,她是一種輕量級的容器。用過EJB的人都知道EJB很複雜,為了一個簡單的功能你不得不編寫多個Java檔案和部署檔案,他是一種重量級的容器。也許你不瞭解EJB,你可能對“輕(重)量級”和“容器”比較陌生,那麼這裡我簡單介紹一下。
1、什麼是容器
“容器”,這個概念困擾我好久。從學習Tomcat開始就一直對此感到困惑。感性的來講,容器就是可以用來裝東西的物品。那麼在程式設計領域就是指用來裝物件(OO的思想,如果你連OO都不瞭解,建議你去學習OO先)的物件。然而這個物件比較特別,它不僅要容納其他物件,還要維護各個物件之間的關係。這麼講可能還是太抽象,來看一個簡單的例子:
程式碼片斷1:
  1. public class Container
  2. {
  3. public void init()
  4. {
  5. Speaker s = new Speaker();
  6. Greeting g = new Greeting(s);
  7. }
  8. }

可以看到這裡的Container類(容器)在初始化的時候會生成一個Speaker物件和一個Greeting物件,並且維持了它們的關係,當系統要用這些物件的時候,直接問容器要就可以了。這就是容器最基本的功能,維護系統中的例項(物件)。如果到這裡你還是感到模糊的話,別擔心,我後面還會有相關的解釋。

2、輕量級與重量級
所謂“重量級”是相對於“輕量級”來講的,也可以說“輕量級”是相對於重量級來講的。在Spring出現之前,企業級開發一般都採用EJB,因為它提供的事務管理,宣告式事務支援,持久化,分佈計算等等都“簡化”了企業級應用的開發。我這裡的“簡化”打了雙引號,因為這是相對的。重量級容器是一種入侵式的,也就是說你要用EJB提供的功能就必須在你的程式碼中體現出來你使用的是EJB,比如繼承一個介面,宣告一個成員變數。這樣就把你的程式碼繫結在EJB技術上了,而且EJB需要JBOSS這樣的容器支援,所以稱之為“重量級”。
相對而言“輕量級”就是非入侵式的,用Spring開發的系統中的類不需要依賴Spring中的類,不需要容器支援(當然Spring本身是一個容器),而且Spring的大小和執行開支都很微量。一般來說,如果系統不需要分佈計算或者宣告式事務支援那麼Spring是一個更好的選擇。

二、 幾個核心概念
在我看來Spring的核心就是兩個概念,反向控制(IoC),面向切面程式設計(AOP)。還有一個相關的概念是POJO,我也會略帶介紹。
1、POJO
我所看到過的POJO全稱有兩個,Plain Ordinary Java Object,Plain Old Java Object,兩個差不多,意思都是普通的Java類,所以也不用去管誰對誰錯。POJO可以看做是簡單的JavaBean(具有一系列Getter,Setter方法的類)。嚴格區分這裡面的概念沒有太大意義,瞭解一下就行。
2、 IoC
IoC的全稱是Inversion of Control,中文翻譯反向控制或者逆向控制。這裡的反向是相對EJB來講的。EJB使用JNDI來查詢需要的物件,是主動的,而Spring是把依賴的物件注入給相應的類(這裡涉及到另外一個概念“依賴注入”,稍後解釋),是被動的,所以稱之為“反向”。先看一段程式碼,這裡的區別就很容易理解了。
程式碼片段2:
  1. public void greet()
  2. {
  3. Speaker s = new Speaker();
  4. s.sayHello();
  5. }

程式碼片段3:
  1. public void greet()
  2. {
  3. Speaker s = (Speaker)context.lookup("ejb/Speaker");
  4. s.sayHello();
  5. }

程式碼片段4:
  1. public class Greeting
  2. {
  3. public Speaker s;
  4. public Greeting(Speaker s)
  5. {
  6. this.s = s;
  7. }
  8. public void greet()
  9. {
  10. s.sayHello();
  11. }
  12. }

我們可以對比一下這三段程式碼。其中片段2是不用容器的編碼,片段3是EJB編碼,片段4是Spring編碼。結合程式碼片段1,你能看出來Spring編碼的優越之處嗎?也許你會覺得Spring的編碼是最複雜的。不過沒關係,我在後面會解釋Spring編碼的好處。
這裡我想先解釋一下“依賴注入”。根據我給的例子可以看出,Greeting類依賴Speaker類。片段2和片段3都是主動的去獲取Speaker,雖然獲取的方式不同。但是片段4並沒有去獲取或者例項化Speaker類,而是在greeting函式中直接使用了s。你也許很容易就發現了,在建構函式中有一個s被注入(可能你平時用的是,傳入)。在哪裡注入的呢?請回頭看一下程式碼片段1,這就是使用容器的好處,由容器來維護各個類之間的依賴關係(一般透過Setter來注入依賴,而不是建構函式,我這裡是為了簡化示例程式碼)。Greeting並不需要關心Speaker是哪裡來的或是從哪裡獲得Speaker,只需要關注自己分內的事情,也就是讓Speaker說一句問候的話。
3、 AOP
AOP全稱是Aspect-Oriented Programming,中文翻譯是面向方面的程式設計或者面向切面的程式設計。你應該熟悉程式導向的程式設計,物件導向的程式設計,但是面向切面的程式設計你也許是第一次聽說。其實這些概念聽起來很玄,說到底也就是一句話的事情。
現在的系統往往強調減小模組之間的耦合度,AOP技術就是用來幫助實現這一目標的。舉例來說,假如上文的Greeting系統含有日誌模組,安全模組,事務管理模組,那麼每一次greet的時候,都會有這三個模組參與,以日誌模組為例,每次greet之後,都要記錄下greet的內容。而對於Speaker或者Greeting物件來說,它們並不知道自己的行為被記錄下來了,它們還是像以前一樣的工作,並沒有任何區別。只是容器控制了日誌行為。如果這裡你有點糊塗,沒關係,等講到具體Spring配置和實現的時候你就明白了。
假如我們現在為Greeting系統加入一個Valediction功能,那麼AOP模式的系統結構如下:
G|RET|TIN|G
V|ALE|DIT|ION
| | |
日誌 安全 事務

這些模組是貫穿在整個系統中的,為系統的不同的功能提供服務,可以稱每個模組是一個“切面”。其實“切面”是一種抽象,把系統不同部分的公共行為抽取出來形成一個獨立的模組,並且在適當的地方(也就是切入點,後文會解釋)把這些被抽取出來的功能再插入系統的不同部分。
從某種角度上來講“切面”是一個非常形象的描述,它好像在系統的功能之上橫切一刀,要想讓系統的功能繼續,就必須先過了這個切面。這些切面監視並攔截系統的行為,在某些(被指定的)行為執行之前或之後執行一些附加的任務(比如記錄日誌)。而系統的功能流程(比如Greeting)並不知道這些切面的存在,更不依賴於這些切面,這樣就降低了系統模組之間的耦合度。

三、 Spring初體驗
這一節我用一個具體的例子Greeting,來說明使用Spring開發的一般流程和方法,以及Spring配置檔案的寫法。
首先建立一個Speaker類,你可以把這個類看做是POJO。
程式碼片段5:
  1. public class Speaker
  2. {
  3. public void sayHello()
  4. {
  5. .out.println("Hello!");
  6. }
  7. }
再建立一個Greeting類。
程式碼片段6:
  1. public class Greeting
  2. {
  3. private Speaker speaker;
  4. public void setSpeaker(Speaker speaker)
  5. {
  6. this.speaker = speaker;
  7. }
  8. public void greet()
  9. {
  10. speaker.sayHello();
  11. }
  12. }

然後要建立一個Spring的配置檔案把這兩個類關聯起來。
程式碼片段7(applicationContext.xml):

相關文章