dwr.xml文件的結構如下:
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"><dwr> <!-- init is only needed if you are extending DWR --> <init> <creator id="..." class="..."/> <converter id="..." class="..."/> </init> <!-- without allow, DWR isn't allowed to do anything --> <allow> <create creator="..." javascript="..."/> <convert converter="..." match="..."/> </allow> <!-- you may need to tell DWR about method signatures --> <signatures> ... </signatures></dwr>
術語
這里是一些必須理解的術語 - 參數會被
converted
,遠程Bean會被
created
。所以如果你有一個叫A的bean,它有一個方法叫A.blah(B) 那么你需要一個A的
creator
和一個B的
converter
。
一:<allow>
allow
段落里面定義的試DWR可以創建和轉換的類。
二:Creators
dwr.xml文件中的create元素的結構如下:
<allow> <create creator="..." javascript="..." scope="..."> <param name="..." value="..."/> <auth method="..." role="..."/> <exclude method="..."/> <include method="..."/> </create> ...</allow>
這里的多數元素都是可選的 - 你真正必須知道的是指定一個creator和一個javascript名字。
creator屬性
是必須的 - 它用來指定使用那種創造器。
默認情況下DWR1.1有8種創造器。它們是:
-
new
: 用Java的new關鍵字創造對象。
-
none
: 它不創建對象,看下面的原因。 (v1.1+)
-
scripted
: 通過BSF使用腳本語言創建對象,例如BeanShell或Groovy。
-
spring
: 通過Spring框架訪問Bean。
-
jsf
: 使用JSF的Bean。 (v1.1+)
-
struts
: 使用Struts的FormBean。 (v1.1+)
-
pageflow
: 訪問Beehive或Weblogic的PageFlow。 (v1.1+)
如果你需要寫自己的創造器,你必須在init部分注冊它。
javascript屬性
用于指定瀏覽器中這個被創造出來的對象的名字。你不能使用Javascript的關鍵字。
scope屬性
非常類似servlet規范中的scope。 它允許你指定這個bean在什么生命范圍。選項有"application", "session", "request" 和"page"。這些值對于Servlet和JSP開發者來說應該相當熟悉了。
scope屬性是可選的。默認是"page"。如果要使用"session"需要cookies。當前的DWR不支持ULR重寫。
param元素
被用來指定創造器的其他參數,每種構造器各有不同。例如,"new"創造器需要知道要創建的對象類型是什么。每一個創造器的參數在各自的文檔中能找到。請查看上面的鏈接。
include和exclude元素
允許創造器來限制類中方法的訪問。一個創造器必須指定include列表或exclude列表之一。如果是include列表則暗示默認的訪問策略是"拒絕";如果是exclude列表則暗示默認的訪問策略是"允許"。
例如要拒絕防范除了
setWibble()
以外的所有方法,你應該把如下內容添加到dwr.xml中。
<create creator="new" javascript="Fred"> <param name="class" value="com.example.Fred"/> <include method="setWibble"/></create>
對于加入到create元素中的類的所有方法都是默認可見的。
auth元素
允許你指定一個J2EE的角色作為將來的訪問控制檢查:
<create creator="new" javascript="Fred"> <param name="class" value="com.example.Fred"/> <auth method="setWibble" role="admin"/></create>
new
創造器在DWR中已經默認聲明了:
<creator id="new" class="uk.ltd.getahead.dwr.create.NewCreator"/>
。你不需要在dwr.xml文件中添加這段話,它已經存在于DWR的內部dwr.xml文件中了。
這個創造器通過類默認的夠早函數創造對象實例。用new創造器有一些好處:
-
安全:DWR創造的對象生存的事件越短,多次調用中間的值不一致的錯誤機會越少。
-
內存消耗低: 如果你的站點用戶量非常大,這個創造器可以減少VM的內存溢出。
你可以通過下面的方式使用new創造器來創造遠程調用Bean:
<allow> <create creator="new" javascript="Blah"> <param name="class" value="java.util.Date"/> </create> ... </allow>
這些代碼把
java.util.Date
映射成Javascript,并且命名為Blah,所以在Javascript中當你調用Blah.toString(reply) 那么一個新的
java.util.Date
就會通過默認的構造函數創造出來, 然后
toString()
方法被調用, 然后結果數據返回給reply方法(在這個例子中date是字符串格式)。
none
'創造器不創建任何對象 - 它會假設你不需要創建對象。這有可能是對的,有兩個原因。
你可能在使用的scope不是"page"(看上面),并在在前面已經把這個對象創建到這個scope中了,這時你就不需要再創建對象了。
還有一種情況是要調用的方法是靜態的,這時也不需要創建對象。DWR會在調用創建器之前先檢查一下這個方法是不是靜態的。
對于上訴兩種情況,你仍然需要class參數,用來告訴DWR它是在操作的對象類型是什么。
scripted
創造器在DWR中已經默認聲明了:<creator id="script" class="uk.ltd.getahead.dwr.create.ScriptedCreator"/>
這個創造器用BSF來執行腳本得到Bean,例如:
<allow> ... <create creator="script" javascript="EmailValidator"> <param name="language" value="beanshell"/> <param name="script"> import org.apache.commons.validator.EmailValidator; return EmailValidator.getInstance(); </param> </create> ...</allow>
script創造器有如下參數:
參數DWR版本描述
language
|
1.0
|
腳本語言,字符串,例如'beanshell'. (必需)
|
script
|
1.0
|
要執行的腳本。 (必需,除非scriptPath參數存在)
|
scriptPath
|
1.1
|
腳本文件路徑。 (必需,除非script參數存在)
|
reloadable
|
1.1
|
是否檢測腳本文件的改動,以重新加載 (可選, 默認true)
|
class
|
1.0
|
創造出對象的類型(可選). 如果沒有DWR通過創造器得到類型。
|
需要了解的主題
要使用這個創造器,你需要把一些輔助庫放到WEB-INF/lib文件夾下:
BSF的jar包
,你要用的
腳本語言的jar包
。
當 一個類是用script創造出來的,并且scope是session或application,如果你的腳本改變,session中的類和script中 的類就不一致了。這樣會出現錯誤。雖然web容器不用重啟,但是用戶需要先登出(或以某種方式清空session),然后再登錄。
當clazz參數不為空,并且用來創造新實例,DWR簡單的調用
class.newInstance()
方法。這種方法是沒問題的,除非腳本正在用某個參數創建一個類,或者調用某個函數來配置這個類。 不幸的是,每次請求都要重新運行script并造成上面的問題。
創造器
讓DWR和Spring一起工作的步驟
-
確認你用的是最新版的DWR。Spring創造器已經有了變化,所以你最好檢查一下DWR的
最新版本
。
-
確認你查看過
開始指南
中的內容。
-
確認你的Spring的Bean在DWR外面運行良好。
-
配置DWR和Spring一起工作。 (看下面)
-
查看演示頁面: http://localhost:8080/[ YOUR-WEBAPP ]/dwr ,檢查spring的Bean是否出現。
DWR對于Spring沒有運行期依賴,所以如果你不使用Spring那么Spring的支持不會產生任何影響到。
The SpringCreator
這個創造器會查找spring的中配置的Bean,用Spring去創建它們。如果你已經在使用Spring,那么這個創造器會非常有用。
你可以通過下面的方式來創建遠程調用的Bean:
<allow> ... <create creator="spring" javascript="Fred"> <param name="beanName" value="Shiela"/> </create> </allow>
尋找你的Spring配置
有三種方式尋找配置文件:
ContextLoaderListener
最 簡單的方式是使用org.springframework.web.context.ContextLoaderListener。你不必使用所有的 Spring-MVC功能,只需要這個Listener就夠了,所以這是個不錯的方案。你需要在WEB-INF/web.xml中做如下配置:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/beans.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
我能找到的ContextLoaderListener的最好的文檔就是
javadoc
。如果你知道還有更好的文檔,請告知我。
Rob Sanheim 指出還有一個能
深入了解ContextLoaderListener
的文檔。
使用location*參數
如 果你要在dwr.xml中指定使用哪些bean,你可以使用location*參數。你可以指定任意多個文件,只要參數以location開始并且唯一即 可。例如:location-1, location-2。這些location被用做Spring的ClassPathXmlApplicationContext的參數:
<allow> ... <create creator="spring" javascript="Fred"> <param name="beanName" value="Shiela"/> <param name="location" value="beans.xml"/> </create> </allow>
直接設置BeanFactory
SpringCreator有一個靜態方法
setOverrideBeanFactory(BeanFactory)
用來通過編程的方式直接設置BeanFactory。
配置DWR和Spring
Bram Smeets寫了一個
有意思的blog
,教你配置DWR使用beans.xml代替WEB-INF/web.xml。
我也對于如何在beans.xml中指定dwr.xml很感興趣,盡管這看上去有些Spring傳染病的感覺。有人知道如何實現嗎?請加入郵件列表并告訴大家。
整合
DWR包括兩個JSF的擴展點,一個創造器和一個ServletFilter。
'jsf'創造器
DWR1.1中有一個體驗版的JsfCreator。你可以哉dwr.xml中這樣使用:
<allow> ... <create creator="jsf" javascript="ScriptName"> <param name="managedBeanName" value="beanName"/> <param name="class" value="your.class"/> </create> ...</allow>
這將允許你通過DWR調用ManagedBean。
The Servlet Filter
DWR/Faces 過濾器允許你不在JSF的生命周期里調用FacesContext中的Bean。
要使用JsfCreator,你應該把DWR/Faces過濾器加到web.xml中。
<filter> <filter-name>DwrFacesFilter</filter-name> <filter-class>uk.ltd.getahead.dwr.servlet.FacesExtensionFilter</filter-class></filter><filter-mapping> <filter-name>DwrFacesFilter</filter-name> <url-pattern>/dwr/*</url-pattern></filter-mapping>
這兩個需要放在web.xml中與其他的filter和filter-mapping放在一起。
pageflow 創造器
DWR中有一個創造器可以和Weblogic或者Beehive中的PageFlow一起工作。
The 'pageflow' Creator
DWR1.1中加入了一個PageFlowCreator。你可以這樣使用:
<allow> ... <create creator="pageflow" javascript="ScriptName"/> ...</allow>
使用靜態方法
DWR會在調用創建器之前先檢查一下這個方法是不是靜態的,如果是那么創造器不會被調用。很顯然這個邏輯適用于所有創造器,盡管如此"null"創造器是最容易配置的。
適用單例類
其他創造器
我么偶爾也需要一些新的創造器,最常見的是一個EjbCreator。討論新的創造器的好地方是在
郵件列表
。
DWR和HttpSessionBindingListeners
DWR1.x中存貯已經創造的Bean的方法需要注意,它在每次請求時都會調用相同的
setAttribute()
方法。就是說,如果一個Bean在dwr.xml中的聲明周期設置為session,再每次調用bean中的方法時,DWR都會執行一次
session.setAttribute(yourBean)
。這看上去沒有什么危害,但是如果你要使用servlet的事件機制的,就是說用了HttpSessionBindingListener接口,你就會發現valueBound和valueUnbound事件在每次調用時都會發生,而不是你想像的在bean被創建時以及session過期時。
DWR2 只在第一次創建對象時調用
setAttribute()
。
三:Converters
我們必須保證所有的參數都可以被轉換。JDK中的多數類型已經有轉換器了,但是你需要給DWR轉換你的代碼的權利。一般來說JavaBean的參數需要一個<convert ...>定義。
-
所有的原生類型 boolean,int,double, 等等
-
原生類型的對象類型 Boolean,Integer,等等
-
java.lang.String
-
java.util.Date 和SQL中的Date
-
以上類型組成的數組
-
以上類型的集合類型 (Lists, Sets, Maps, Iterators, 等)
-
從DOM, XOM, JDOM 和 DOM4J中的DOM對象 (類似 Element 和 Document)
下面這些轉換器有單獨章節介紹
基礎的轉換器
原生類型,String,像BigDecimal這樣的簡單對象的轉換器已經有了。你不需要在dwr.xml中<allow>部分的<convert>中定義。它們默認支持。
默 認支持的類型包括: boolean, byte, short, int, long, float, double, char, java.lang.Boolean, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Character, java.math.BigInteger, java.math.BigDecimal 和 java.lang.String
Date轉換器
Date 轉換器負責在Javascript的Date類型與Java中的Date類型(java.util.Date, java.sql.Date, java.sql.Times or java.sql.Timestamp)之間進行轉換。同基礎的轉換器一樣,DateConverter默認是支持的。
如果你有一個 Javascript的字符串 (例如"01 Jan 2010") ,你想把它轉換成Java的Date類型有兩個辦法:在javascript中用Date.parse()把它解析成Date類型,然后用DWR的 DateConverter傳遞給服務器;或者把它作為字符串傳遞給Server,再用Java中的SimpleDateFormat(或者類似的)來解 析。
同樣,如果你有個Java的Date類型并且希望在HTML使用它。你可以先用SimpleDateFormat把它轉換成字符串再使 用。也可以直接傳Date給Javascript,然后用Javascript格式化。第一種方式簡單一些,盡管浪費了你的轉換器,而且這樣做也會是瀏覽 器上的顯示邏輯受到限制。其實后面的方法更好,也有一些工具可以幫你,例如:
其他對象
其實創建自己的轉換器也很簡單。Converter接口的Javadoc包含了信息。其實這種需要很少出現。在你寫自己的Converter之前先看看BeanConverter,它有可能就是你要的。
四:<init>
可選的init部分用來聲明創造bean的類和轉換bean的類。多數情況下你不需要用到他們。如果你需要定義一個新的Creator [
JavaDoc
] 和 Converter [
JavaDoc
] , 那么你就需要在這里定義他們。但是建議你現檢查一下DWR是不是已經支持了。
在init 部分里有了定義只是告訴DWR這些擴展類的存在,給出了如何使用的信息。這時他們還沒有被使用。這中方式很像Java中的import語句。多數類需要在 使用前先import一下,但是只有import語句并不表明這個類已經被使用了。每一個creator和converter都用id屬性,以便后面使 用。
五:<signatures>
DWR使用反射來找出在轉換時應該用那種類型。有時類型信息并不明確,這時你可以在這里寫下方法的簽名來明確類型。
signatures段使DWR能確定集合中存放的數據類型。例如下面的定義中我們無法知道list中存放的是什么類型。
public class Check{ public void setLotteryResults(List nos) { ... }}
signatures段允許我們暗示DWR應該用什么類型去處理。格式對以了解JDK5的泛型的人來說很容易理解。
<signatures> <![CDATA[ import java.util.List; import com.example.Check; Check.setLotteryResults(List<Integer> nos); ]]></signatures>
DWR中又一個解析器專門來做這件事,所以即便你的環境時JDK1.3 DWR也能正常工作。
解析規則基本上會和你預想規則的一樣(有兩個例外),所以java.lang下面的類型會被默認import。
第一個是DWR1.0中解析器的bug,某些環境下不能返回正確類型。所以你也不用管它了。
第二個是這個解析器時"陽光(sunny day)"解析器。就是說它非常寬松,不想編譯器那樣嚴格的保證你一定正確。所以有時它也會允許你丟失import:
<signatures> <![CDATA[ import java.util.List; Check.setLotteryResults(List<Integer>); ]]></signatures>
將來的DWR版本會使用一個更正式的解析器,這個編譯器會基于官方Java定義,所以你最好不要使用太多這個不嚴格的東西。
signatures段只是用來確定泛型參數中的類型參數。DWR會自己使用反射機制或者運行時類型確定類型,或者假設它是一個String類型。所以:
不需要signatures - 沒有泛型參數:
public void method(String p);public void method(String[] p);
需要signatures - DWR不能通過反射確定:
public void method(List<Date> p);public void method(Map<String, WibbleBean> p);
不需要signatures - DWR能正確的猜出:
public void method(List<String> p);public void method(Map<String, String> p);
不需要signatures - DWR可以通過運行時類型確定:
public List<Date> method(String p);
沒 有必要讓Javascript中的所有對象的key都是String類型 - 你可以使用其他類型作為key。但是他們在使用之前會被轉換成String類型。DWR1.x用Javascript的特性把key轉換成String。 DWR2.0可能會用toString()方法,在服務段進行這一轉換。
六:多個dwr.xml文件
可以有多個dwr.xml文件(詳細信息見
web.xml文檔
)。每個文件中的定義會被加在一起。DWR用這個功能來加載基礎配置文件。我們可以看看標準被配置文件來了解dwr.xml的內容。
dwr.xml詳解