DWR學習(一)

fightplane發表於2007-09-26

http://getahead.ltd.uk/dwr/
這段時間較閒,研究了一番dwr.發現dwr實現的AJAX有些地方確實很是先進.比如動態生成javascript程式碼;隱藏的http協議;javascript於java程式碼互動的是javascript物件(或字串)等.
以下是我臨時譯的一些東西.本來想全譯,發現dwr實在是簡單,就隨便寫了.英文居差,現一把.

1、DWR: Easy AJAX for JAVA

作為一個java open source library,DWR可以幫助開發人員完成應用AJAX技術的web程式。它可以讓瀏覽器上的javascript方法呼叫執行在web伺服器上java方法。

DWR主要由兩部門組成。javascript與web伺服器通訊並更新web頁;執行在web伺服器的Servlet處理請求並把響應發回瀏覽器。

DWR採用新穎的方法實現了AJAX(本來也沒有確切的定義),在java程式碼基礎上動態的生成javascript程式碼。web開發者可以直接調 用這些javascript程式碼,然而真正的程式碼是執行在web伺服器上的java code。出與安全考慮,開發者必須配置哪些java class暴露給DWR.(dwr.xml)

這種從(java到javascript)呼叫機制給使用者一種感覺,好象常規的RPC機制,或RMI or SOAP.但是它執行在web上,不需要任何瀏覽器外掛。

DWR不認為瀏覽器和web伺服器之間協議重要,把系統介面放在首位。最大挑戰是java method call的同步特徵與ajax非同步特性之間的矛盾。在非同步模型裡,結果只有在方法結束後才有效。DWR解決了這個問題,把回撥函式當成引數傳給方法,處理 完成後,自動呼叫回撥方法。

這個圖表顯示了,通過javascript事件,DWR能改變select的內容,當然這些內容由java程式碼返回。 javascript函式Data.getOptions(populateList)由DWR動態生成,這個函式會呼叫java class Data類的方法。DWR處理如何遠端呼叫,包括轉換所有的引數和返回的結果(javascript/java)。java方法執行完後,執行回撥方法 populateList。在整個過程中我們就想在用本地的方法一樣。

2、Getting Started

廢話少說,試試就ok了。
web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app id="dwr">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
</web-app>

dwr.xml 與web.xml同目錄
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<create creator="new" javascript="JDate">
<param name="class" value="java.util.Date"/>
</create>
</allow>
</dwr>

index.html
<html>
<head>
<title>DWR - Test Home</title>
<script type='text/javascript' src='dwr/interface/JDate.js'></script>
<script type='text/javascript' src='dwr/engine.js'></script>
<script>
function init(){
JDate.getYear(load);
}
function load(data){
alert(data+1900+'年')
}
</script>
</head>
<body onload="init()">
</body>
</html>

dwr.jar 下載放lib下

完了,什麼,夠了,就這些。訪問ok!
3、Examples
http://www.aboutmyhealth.org/ 這不是Google Suggest嗎!ok.
4、原始碼淺析
dwr的設計很象webwork2的設計,隱藏http協議,擴充套件性,相容性及強。

通過研究uk.ltd.getahead.dwr.DWRServlet這個servlet來研究下dwr到底是如何工作滴。

程式碼
  1. web.xml配置  
  2. <servlet>  
  3.     <servlet-name>dwr-invoker</servlet-name>  
  4.     <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>  
  5.   </servlet>  
  6.   <servlet-mapping>  
  7.     <servlet-name>dwr-invoker</servlet-name>  
  8.     <url-pattern>/dwr/*</url-pattern>  
  9. </servlet-mapping>  

這樣所有的/dwr/*所有請求都由這個servlet來處理,它到底處理了些什麼能。我們還以上面最簡單的例子來看。
1、 web伺服器啟動,DWRServlet init()方法呼叫,init主要做了以下工作。
設定日誌級別、例項化DWR用到的單例類(這些類在jvm中只有一個例項物件)、讀去配置檔案(包括dwr.jar包中的dwr.xml,WEB-INF/dwr.xml. config*.xml)。
2、請求處理
DWRServlet.doGet, doPost方法都呼叫processor.handle(req, resp)方法處理。Processor物件在init()方法中已經初始化了。
程式碼
  1. public void handle(HttpServletRequest req, HttpServletResponse resp)  
  2.         throws IOException  
  3.     {  
  4.         String pathinfo = req.getPathInfo();  
  5.         if(pathinfo == null || pathinfo.length() == 0 || pathinfo.equals("/"))  
  6.         {  
  7.             resp.sendRedirect(req.getContextPath() + req.getServletPath() + '/' + "index.html");  
  8.         } else  
  9.         if(pathinfo != null && pathinfo.equalsIgnoreCase("/index.html"))  
  10.         {  
  11.             doIndex(req, resp);  
  12.         } else  
  13.         if(pathinfo != null && pathinfo.startsWith("/test/"))  
  14.         {  
  15.             doTest(req, resp);  
  16.         } else  
  17.         if(pathinfo != null && pathinfo.equalsIgnoreCase("/engine.js"))  
  18.         {  
  19.             doFile(resp, "engine.js""text/javascript");  
  20.         } else  
  21.         if(pathinfo != null && pathinfo.equalsIgnoreCase("/util.js"))  
  22.         {  
  23.             doFile(resp, "util.js""text/javascript");  
  24.         } else  
  25.         if(pathinfo != null && pathinfo.equalsIgnoreCase("/deprecated.js"))  
  26.         {  
  27.             doFile(resp, "deprecated.js""text/javascript");  
  28.         } else  
  29.         if(pathinfo != null && pathinfo.startsWith("/interface/"))  
  30.         {  
  31.             doInterface(req, resp);  
  32.         } else  
  33.         if(pathinfo != null && pathinfo.startsWith("/exec"))  
  34.         {  
  35.             doExec(req, resp);  
  36.         } else  
  37.         {  
  38.             log.warn("Page not found. In debug/test mode try viewing /[WEB-APP]/dwr/");  
  39.             resp.sendError(404);  
  40.         }  
  41.     }  

哦。這些恍然大悟。dwr/*處理的請求也就這幾種。
(1)dwr/index.html,dwr/test/這種只能在debug模式下使用,除錯用。
dwr/engine.js,dwr/util.js,dwr/deprecated.js當這個請求到達,從dwr.jar包中讀取檔案流,響應回去。(重複請求有快取)
(2)當dwr/interface/這種請求到來,(例如我們在index.html中的 <script type='text/javascript' src='dwr/interface/JDate.js'></script>)DWR做一件偉大的事。把我們在WEB- INF/dwr.xml中的
<create creator="new" javascript="JDate">
<param name="class" value="java.util.Date"/>
</create>
java.util.Date轉化為javascript函式。
http://localhost:port/simpledwr/dwr/interface/JDate.js看看吧。
細節也比較簡單,通過java反射,把方法都寫成javascript特定的方法。(我覺得這些轉換可以放到快取裡,下次呼叫沒必要再生成一遍,不知道作者為什麼沒這樣做)。
(3)dwr/exec
javascript呼叫方法時傳送這種請求,可能是XMLHttpRequest或IFrame傳送。
當然,javascript呼叫的方法簽名與java程式碼一致,包括引數,還有javascript的回撥方法也傳到了伺服器端,在伺服器端很容 易實現。回撥方法的java的執行結果 返回類似 <script>callMethod(結果)<script>的javascript字串,在瀏覽器執行。哈,一切就這麼簡 單,巧妙。

dwr的設計構思很是巧妙。
第一、把java類轉化為javascript類由dwr自動完成,只需簡單的配置。
第二、應用起來極其簡單。開發者不要該伺服器程式碼就可以整合。
第三、容易測試。和webwork一樣,隱藏的http協議。
第四、及強擴充套件性。例如與spring整合,只需修改一點程式碼。
第五、效能。就我與jason,等簡單比較,dwr效能可能是最好的。
第六、自動把java物件轉化為javascript物件,並且及易擴充套件。[/code]

 

相關文章