Java平台已經逐漸發展為一個成熟可靠的公司專屬應用程式平台,成熟的應用平台的一個標誌則是它能夠帶動大量的衍生技術以及可以與其他技術整合的選項。本文將詳細講述怎樣用Grails這項傳統JEE應用開發的衍生技術,結合另一項完全不同但卻可以在Java中使用的Flex技術來開發JEE。這兩個平台都能大幅度提高開發效率。兩者相結合則在為J2EE應用建立富用戶端的同時不影響整體的開發效率。
Grails的前身是一個在JVM中啟動並執行web應用,它使用Groovy以及其它幾個著名的架構,比如Spring和Hibernate。為了實現快速應用開發,它極為依賴“Convention over Configuration”原則。Groovy擁有很多動態特性,在定義組件間共同行為方面,功能非常強大。Grails採用plug-in構架,因此很容易把它與其他框架組成,而且也很容易在應用間複用各自的功能。
Flex是個RIA開發套件,由它建立的SWF應用只能在FlashPlayer下應用。這是Adobe(前身為MacroMedia)的一個新型 Flash開發套件。除了擁有豐富的widget和把各種widget粘合在一起的強大的語言之外,它還能提供一些高端通訊解決方案,分布式應用程式的開發因此變得相當容易。它使用兩種文法:MXML和ActionScript。MXML建立在XML文法之上,專門用來定義萬用群組件的使用者介面;而 ActionScript則用來定義組件之間的動態互動。
Grails和Flex的整合——難題所在
要把Grails和Flex這兩個建立在完全不同基礎上的架構結合起來,首先會遇到諸多通訊方面的問題:
一個架構中的組件如何才能在另一個架構中找到正確的通訊對象?
從本質上來說,Grails實際是運行在伺服器的JVM上的一個web應用程式框架。Flex則是擁有用戶端和(瘦)伺服器組件的RIA平台,伺服器組件以web應用的方式部署。因此,這兩個架構之間的整合實際上在web應用程式容器內進行。
使用者在Flex UI發起的通訊必須通過Grails組件來調用商務邏輯。那麼,Flex UI組件該如何找到正確的Grails組件呢?
架構間如何解析彼此的資料?
Flex採用ActionScript來描述資料,而Grails則採用Java和Groovy對象。Flex UI向伺服器發送的ActionScript對象應該被轉述為應用程式能夠理解的資料結構。這該如何??
某個使用者的修改該如何與該應用程式的其他使用者互動?
這是多使用者應用程式普遍存在的問題,但同時運用兩個不同的架構使得問題更加複雜。痛點在於Grails應用程式,使用者通過Flex UI來啟動這個應用,但如何通過Flex UI與其他使用者通訊,讓他們知道該使用者的這一動作呢?
在接下來的三個部分中,我們詳細討論上文提到的三個問題,尋找採用Grails和Flex的解決方案。
整合——尋找訊息接收對象
一個架構中的組件如何才能在另一個架構中找到正確的通訊對象呢?
具體到Grails和Flex的話,這個問題其實就是在問Flex組件怎樣才能找到正確的Grails組件,進而發送請求資料,或者以使用者的名義執行一些操作。為了更好的理解解決這個痛點的方法,我們首先來瞭解一下Flex的通訊子系統。
Flex中的客戶——伺服器通訊
Flex的通訊子系統可以分為客戶和伺服器兩個部分。客戶部分包含了那些允許應用程式發送或者接受訊息的組件,比如RemoteObject和 Consumer組件。這些組件與伺服器部分特定的“服務”對象相關聯,比如RemotingService和MessagingService。客戶組件及其相關聯的伺服器組件的結合能夠支援典型的通訊模式。比方說結合Consumer、Producers和MessagingService,應用軟體就能夠使用Publish-Subscribe機制來通訊。
客戶和伺服器件的通訊通過通道(Channel)來完成。通道的實現方式並不唯一,所有通道中最重要的是AMFChannel和RTMPChannel。AMFChannel建立在HTTP基礎上,也就是說建立在請求-回複的構架上。它可以和MessagingService同時使用,從而支援 Publish-Subscribe構架。這種結合下,通道定期從發布中讀取新的訊息,產生請求。RTMPChannel在這樣的配置下效率更高,它能夠在TCP/IP的基礎上支援客戶與伺服器間的串連。這樣一來,客戶與伺服器之間能夠立即發送或接受訊息。遺憾的是,Adobe免費開源的Flex實現—— BlazeDS不包含這樣的RTMPChannel實現。
Flex中最重要的通訊基礎設施是Destinations。Destination是通訊通道的伺服器端終點。一個服務提供一個 Destination,而客戶組件則通過這個Destination與這個服務相關聯。關聯的客戶組件可以向Destination發送和讀取訊息。Destinations可以由Factories建立。
Grails暴露的遠程介面:服務
如何把Flex複雜的通訊設施和Grails結合起來呢?Grails能夠識別幾類對象:域對象、控制器、視圖和服務。Grails中的每個服務都是通過外部通訊通道——比如HTTP——展示某些功能或者服務的一個對象。而在Flex中,每個服務則與一個Destination相對應。
這恰恰就是針對Grails的flex-plugin所提供的解決方案。Grails中所有註明向Flex展示的服務都將在Flex架構中以Destination的形式註冊。Grails通過一個特定的 Factory把註明的服務添加到Flex中特別配置的RemotingService。這個特定的Factory會在Grails使用的Spring上下文中定位到對應的服務。所有這些配置都可以在services-config.xml中找到,flex-plugin會為Grails將這個檔案複製到正確的地方。
class UserService {
static expose = ['flex-remoting']
def List all() {
User.createCriteria().listDistinct {}
}
def Object get(id) {
User.get(id);
}
def List update(User entity) throws BindException {
entity.merge();
if (entity.errors.hasErrors()) {
throw new BindException(entity.errors);
}
all();
}
def List remove(User entity) {
entity.delete();
all();
}
}
這段配置將UserService展示給flex客戶。下面這段MXML代碼則是對前面這段配置的應用。RemoteObject的 destination是userService,這個userService正是Grails中目標對象提供的服務名。服務物件的所有方法這下都可以作為遠程操作調用。ActionScript可以將這些操作像一般的方法那樣調用,而方法調用的結果或錯誤也可以當作一般的ActionScript事件來處理。
...
<mx:RemoteObject id="service" destination="userService">
<mx:operation name="all" result="setList(event.message.body)"/>
<mx:operation name="get" result="setSelected(event.message.body)"/>
<mx:operation name="update"/>
<mx:operation name="remove"/>
</mx:RemoteObject>
...結論
flex-plugin為Grails提供的針對整合的解決方案非常漂亮,便於使用而且幾乎是自動化的。在Convention-over-Configuration概念下,Destinations動態添加到Flex配置的時候使用命名規範。