DWR幫助文件-dwr.xml檔案的配置

fightplane發表於2007-09-27
也許朋友們會以為這是DWR官方釋出的什麼幫助,但非常遺憾這不是。現在不少朋友在使用DWR開發專案,我也是其中之一,但苦於關於DWR的幫助文件實在太少,很多問題都不得不自己去鑽研DWR的原始碼才能解決或理解。經過一段時間的苦苦鑽研,總結出那麼一點點心得,現在從DWR原始碼實現的角度詳細講解DWR的使用,寫出來與大家分享。今天我先講一講dwr.xml的配置。

一、為什麼要配置dwr.xml

要理解dwr.xml的配置首先要理解DWR的基本功能。DWR其功能的強大在於它可以用一種前所未有的便利方式將前端頁面中的js與後端伺服器中的java程式進行直接的轉換。比如,它可以將java程式中的某個XxxBus轉換成js中的一個物件,並將XxxBus中的某個方法如getXXX()轉換成js中的一個名為XxxBus.getXXX()function。同時,它可以將java中的值物件或其它資料包轉換成js中的物件。理解這一點非常重要。配置檔案dwr.xml就是讓你告訴DWR哪些Bus你需要在頁面中呼叫,以及你需要呼叫這些Bus中的哪些方法,哪些方法你不需要呼叫。一旦告訴了DWR這些資訊,你似乎就可以在頁面中像寫java程式一樣的直接呼叫Bus,而跳過了MVC層(如struts中的actionactionform)以及MVC層的繁瑣配置與程式設計。為了幫助你理解這一點我舉一個例子,已經清楚的朋友可以跳過。

假如我在專案的BUS層寫了一個類叫DepartmentBus並在這個類中定義了一個方法getDepartment(String id),那麼我們先把這個類和它的方法配置到dwr.xml中(如何配置我後面再講)。當DWR啟動的時候,它會將java中的DepartmentBus及其方法getDepartment(String id)轉換成js中的DepartmentBus及其方法getDepartment(String id)。更具體地說,它會動態產生一個叫DepartmentBus.js的檔案(這個檔案可以在測試狀態下從“專案地址埠/dwr/test/DepartmentBus”中看到)。在這個檔案中,會建立一個DepartmentBus的物件,一個叫DepartmentBus.fincDepartment(p0, callback)的函式。如果你在頁面中引入了DepartmentBus.js檔案,你就可以在該頁面的<script></script>中呼叫DepartmentBus.fincDepartment(p0, callback)方法,DWR就可以通過Ajax的方式直接去呼叫伺服器端的DepartmentBus中的fincDepartment(String id)。同理,如果DepartmentBus中還有其它的函式,如createDepartment(Department vo)updateDepartment(Department vo)等,DWR也會做如此的轉換。

前面我們提到,DWR可以將伺服器端java中的物件及其方法轉換成客戶端的js中的物件及其方法,一個棘手的問題就出現了。在java中的許多方法都是將某個物件作為它引數或返回值。這些物件往往都是一個個的JOPO,即它們有許多的屬性,其中包含了需要傳遞的資料(最典型的就是值物件)。當js在呼叫java的某個方法時需要提供這個物件引數,同時在呼叫完成時需要將物件返回值以js可以接受的方式返回。怎樣做到這一點呢?最簡單的方式當然是讓DWR再做一次轉換,將這些java物件轉換成js的物件,或者將js的物件轉換成java物件。Ok,經過DWR這樣的兩次轉換,我們就可以在頁面與後臺間自由地進行函式呼叫和資料交換。

二、如何配置dwr.xml檔案

講到這裡,我們現在重新回到dwr.xml檔案。前面我們提到,如果你需要在頁面中呼叫伺服器端的某個類中的方法,你需要在dwr.xml中註冊這個類及其方法,註冊方法如下:

  1. <create creator="spring" javascript="DepartmentBus" scope="script">  
  2.       <param name="beanName" value="departmentBus"/>  
  3. create>  

Creator是建立這個物件所使用的構建器,如果你希望使用傳統的new方法就寫成new,如果你希望使用spring來建立則寫成spring。當然也有其它的建立方法,有哪些方法呢?你可以開啟dwr.jar,在org.directwebremoting包中找到一個dwr.xml的檔案。在該檔案init下的create中可以看到。

Javascript是用於你在js中呼叫這個物件時使用什麼名稱,強烈建議你使用首字母大寫,這樣在頁面中很容易看出這是一個與後臺對應的物件。

如果你使用了spring並且在creator中選擇了spring,那麼你需要一個叫beanName的引數,而其值就是在spring配置檔案中配置的beanName,如departmentBus

另外2個非常有用的引數是excludeincludeexclude可以禁止頁面呼叫後臺的某個或某些方法,具體的寫法是在create中加入:

  1. <exclude method="createDepartment"/>  

method部分寫的是這些方法不帶括號的方法名,如果有多個就寫多行excludeinclude則規定頁面只能呼叫某些方法。

dwr.xml中註冊了需要呼叫的物件及其方法以後,你應當註冊其所有方法的引數和返回值所涉及的物件,註冊方法如下

xml 程式碼
  1. <convert match="com.htxx.demo.datasource1.model.Department"    
  2. javascript="Department" converter="hibernate3"/>  

match用於告訴DWR你將需要把java中的哪些類轉換成js。你可以寫成com.htxx.demo.model.*,但我並不推薦大家這樣使用。為什麼呢?如果你像前面那樣一個一個地註冊物件,則就可以在頁面使用這個的語句初始化一個物件:

var dep = new Department();

如果你使用後一種方法去批量註冊物件,那麼你就不能這樣初始化這個物件而只能這樣手動註冊:

Var dep = {departmentId:null, departmentName:null, ……};

我推薦大家採用第一種方法的好處可以在我後面寫的《DWR幫助說明-如何編寫通用的單行編輯框》充分展現出來。但採用這種方法在DWR的現有版本中似乎還有點兒BUG,如何解決這個BUG我也將在後面的《DWR幫助說明-dwr的bug及其解決方法》中解答。

Javascript用於說明你在頁面中使用這個物件的名稱,也強烈建議大家使用首字母大寫。

Converter用於告訴DWR用什麼DWR的類來執行轉換,常用的轉換器有beanobjecthibernate2hibernate3等。DWR有哪些轉換器可以在dwr.jardwr.xml(該檔案的位置見前文)中找到。我需要強調的是,如果朋友們使用了hibernate,那麼你需要將需要使用的所有值物件都通過轉換器註冊。但是我在網上看見很多朋友都使用bean轉換器來註冊。如果你使用bean來轉換值物件,在執行程式的時候會出現很多問題(這些問題我就不詳述了),同時還會出現效率的問題,因為DWR會將該值物件的所有屬性,及其這些屬性的所有屬性,所有屬性的屬性,都以窮舉的方式取出來。熟悉hibernate的朋友應當馬上明白這樣將是資料庫操作的一個災難。如果你使用hibernate2hibernate3作為轉換器將不會發生這樣的事,同時,hibernate3還較好地解決了延遲查詢的問題,但DWR官方建議我們使用hibernateopenSessionInViewFilter,這我也不再詳述,不清楚的朋友可以查閱hibernate3的幫助文件。但另一個問題我不得不提,DWR在使用延遲查詢的時候其實還是有問題的。譬如有一個值物件Employee包含一個Department的屬性,根據延遲查詢的規則,在get某個Employee時,屬性Department不會馬上裝載,即使執行getDepartment()也不會裝載。必須到真正對這個Department操作的時候才會裝載。既然如此,問題就來了,我們使用DWR執行查詢的時候,常常是真正到頁面才會讀取Department,這時已經是脫離伺服器端到頁面端了而不能再得到Department。這個問題怎麼辦呢,最好的辦法是在伺服器端就提前裝載頁面需要使用的屬性,因為作為開發者他肯定知道哪些屬性要在客戶端使用,哪些屬性不需要。至於如何在伺服器端就提前裝載,感興趣的朋友在我的示例中看到。

另外一個需要提的是,與creator一樣,轉換器也可以一樣地設定excludeinclude引數。但是與creator不同的是,它們說明DWR在轉換物件的時候需要轉換或不轉換某些屬性。這個引數對於hibernate的一對一關聯非常重要。在hibernate中一對一關聯是不做延遲查詢的,假如有一個值物件Employee與值物件Address是一對一關聯,那麼Employee中有Address的屬性,而Address中有Employee的屬性。由於一對一關聯不做延遲查詢,當DWR在轉換一個Employee是會裝載它的屬性Address,然後在裝載Address的時候,又會去裝載Address中的Employee屬性。如此這樣,就會形成一個死迴圈,最後以堆疊溢位告終。解決這個問題的辦法就是禁掉Address中的Employee屬性,避免產生死迴圈。具體寫法如下:

xml 程式碼
  1. <convert match="com.htxx.demo.model.Address" javascript="Address" converter="hibernate3">  
  2.     <param name="exclude" value="employee"/>  
  3. </convert>  

Value部分是需要轉換的屬性,如果有多個則用逗號隔開就可以了。一個dwr+spring+hibernate的示例

 

相關文章