本文的主要內容是對 Grails 1.1 中 PetClinic-MVC 實例的代碼分析。這個實例主要驗證了如何在非 Grails
的項目中使用 GORM。此外也演示了 Spring MVC 2.5 的一些用法。本文假設大家對 Spring、Spring
MVC、Groovy 和 Grails 有著初步的了解。OK, let's go!
Domain 層
目前 Grails 只提供了在普通 Spring 應用中獨立使用 GORM 的方法(現在 Groovy 和 Grails 已是
SpringSource 旗下的產品了,自然要首先對自家產品提供支持)。不過相信以后在其它 Java EE 框架中也可獨立使用 GORM。
在 Grails 以外使用 GORM,只需在 Domain 類上使用 grails.persistence.Entity 注釋即可。其它的就像在 Grails 中的一樣。
在配置方面也很簡單,只要使用 <gorm:sessionFactory ... /> 即可。詳細請見 Grails 的 PetClinic,這里就不拗述了。
Controller 層
同 Domain 層一樣,Controller 層也全部使用 Groovy 實現。主要是因為 GORM 的大部分方法都是動態的。
1. ClinicController: ClinicController 實現了 InitializingBean 接口。這個接口只有一個方法 afterPropertiesSet。這個方法會在 Bean 完成屬性設置之后執行。PetClinic 用這個方式實現了類似 Grails 中 BootStrap 的部分功能。在這個方法中我們可以看到 GORM 中的 save() 方法和 addTo* 動態方法都是可用的。
可能有人會問,afterPropertiesSet()
這個方法的作用就是增加一些預定義的數據,為什么不使用構造函數來實現。實際上,在本例中,afterPropertiesSet()
的作用的確可以用構造函數來實現(經過測試)。至于作者是出于什么目的而選擇了實現 InitializingBean 接口我也說不好。
ClinicController 的其它部分就是一些簡單的不能再簡單的 Http 請求處理函數了。需要說明的一點是 @RequestMapping 中的值代表了請求的 URL,這個是不用帶后綴的,也就是說 ".do" 是多余的。
2. AddPetForm:
在 AddPetForm 類的級別上有三個注釋,分別是 @Controller, @RequestMapping 和
@SessionAttributes。@Controller 不用多說,作用是這個類聲明為一個 Spring Controller,并添加到
Context 中。下面介紹一下 @RequestMapping 和 @SessionAttributes 的功能:
除此以外,AddPetForm 中還有兩個特殊的方法,一個是注釋了 @ModelAttribute 的方法;另一個是注釋了 @InitBinder 的方法。下面分別介紹一下:
Controller 層中兩個特殊的類:ClinicBindingInitializer 和 PetTypeEditor
配置
1. applicationContext.xml
applicationContext 中大部分都是常見的 Spring 配置。特殊一點的配置除了 <gorm:sessionFactory ... /> 以外,還有 Hibernate StatisticsService JMX 的配置。
2. dispatcher-servlet.xml
Spring MVC 配置文件(也是一個 Spring 的配置文件)的默認名是 dispatcher-servlet.xml。
從上至下,第一個配置表明哪個 Package 下的 Class 使用了 Spring 的 @Controller
注釋。這樣 Spring 容器在啟動的時候會自動掃描其下的 Class,將加注了 @Controller 注釋的類加入到 Spring
的容器中。
第二個配置 DefaultAnnotationHandlerMapping 是 HandlerMapping 接口的一個最常用的實現類。用于映射 Handler 和請求路徑之間的關系。通過這樣的配置,Controller 中的 @RequestMapping 中的路徑值就可以真正地和 Http 請求對應起來。HandlerMapping 的另外一個常用的實現類是 ControllerClassNameHandlerMapping 。
第三個配置 AnnotationMethodHandlerAdapter 的作用是使處理請求的 Handler 能夠真正的具有處理請求的能力而提供一些服務,包括為請求參數綁定應用相應的屬性編輯器(通過注冊 webBindingInitializer 實現)。
==============================結束的分割線==============================
寫到這里,PetClinic-MVC 中值得分析學習的代碼都提到了。PetClinic-MVC 是一個很小的應用,但是其中演示了 GORM 在普通 Spring 中的應用和 Spring MVC 2.5 的使用。細看的話還是能學到不少東西的。我所寫的只是我的理解,難免會有一些不足和錯誤,歡迎糾正。
本文將簡單談談我對 EJB 3.0 的兩種 Persistence Context 和 Seam-managed Persistence Context 的不同點的理解、所要解決的問題和我自己所疑惑的問題。
EJB 3.0 (JPA) 的 Persistence Context
大家在使用 EJB 3.0 的時候會注意到 EJB 3.0 中的容器管理 Persistence Context
有兩種類型,一種是 Transaction,另一種是 Extended。這是一個較 Hibernate 的 Session
所沒有的概念,Session 沒有兩種不同的類型,而且最重要的是 Session 不是容器管理的,這里的容器指的是 App Server
容器。這里暫時不談論 Persistence Context 與 Session 之間的異同,主要談談兩種 Persistence
Context 之間的不同。學過 ORM 的同學都知道,當 Persistence Context 是打開狀態的時候,Model
就處于被管理的狀態中;當 Persistence Context 關閉之后,Model 就處于了 Detached 狀態。
上面這些特性對于 Transaction 或 Extended 的 Persistence Context
都是一樣的,不同的地方在于 Persistence Context 何時被打開關閉。由于絕大多數情況下 Persistence Context
是被容器管理的(如果你不嫌累也可以自己控制 Persistence Context),所以在 EJB 3.0 應用中看不到打開或關閉
Persistence Context 的代碼(Spring + Hibernate 的應用也同樣如此,Hibernate Session
的管理工作可以交給 Spring 來做)。
其實,Transaction 和 Extended Persistence Context
的不同之處也就在于容器何時打開或關閉 Persistence Context。Transaction 類型的 Persistence
Context 的打開和關閉是和事務的打開和關閉是同步的。也就是說在一個事務開始之后,Persistence Context
才會開始;在事務關閉的時候,相應的 Persistence Context 也會被關閉。
Extended 類型的 Persistence Context 的打開和關閉是和 Stateful Session Bean
的生命周期同步的,是跨越事務的。也就是說,從 SFSB 的初始化開始,直到銷毀,Persistence Context
都是存在的。你可以在事務之外執行寫操作,但是這是并不會執行真正的數據庫操作,寫操作只是放入了隊列,直到下一個事務,寫操作才會真正地被執行。兩者的
不同簡單說來就是 Extended Persistence Context 存在的時間更長。那為什么要有兩種不同的 Persistence
Context 呢?
當一個 Web
請求到來時,服務器會打開一個線程,這個線程可能會調用一個事務方法,這是一個事務便開始了,當這個請求結束時,線程關閉,事務也隨之結束。由于
Transaction 類型的 Persistence Context 的生存周期是在事務范圍之內的,所以一個 Web
請求的結束也意味著相應的 Persistence Context 的關閉。由于多數 Web 應用在一次 Web
請求內即可完成一個獨立的操作,所以大部分情況下 Transaction 的 Persistence Context
是適用的。但是對于一些復雜的應用,一次操作需要跨越多次請求。這種情況下,如果依舊使用 Transcation 的 Persistence
Context,由于每次請求結束后,相應的 Persistence Context 都被關閉,相應的 Model 也就變為 Detached
狀態。如果接下來的請求仍然需要這些已經變為 Detached 狀態的 Model 就需要重新 load,使用 merge()
方法來持久化。稍有不適就會產生 LazyInitializationException 和
NonUniqueObjectException。同時,這也提高了操作的復雜程度。
如果使用 Extended Persistence Context 就能解決這些問題。由于 Extended
Persistence Context 的生命周期是與 SFSB 的生命周期同步的,所以只要多次請求調用的都是同一個 SFSB
中的方法,有多少次的請求,Persistence Context 總是同一個,其中的 Model 也始終是被管理的。很好地解決了
Persistence Context 在線程之間傳遞的問題,也不會有 LazyInitializationException 和
NonUniqueObjectException 問題的發生。
Seam-managed Persistence Context
EJB 3.0 容器管理之下的 Persistence Context
很不錯,能解決很多問題,但是還是有些問題無法解決。Seam 很強大,如果有些問題 EJB 容器解決不了了,沒關系,把 Persistence
Context 交由 Seam 來管理就 OK 了。那 Seam 都能解決哪些 EJB 不能解決的問題呢?先考慮下面兩個問題: