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
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>
<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到底是如何工作滴。
Code
web.xml配置
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
</servlet>
<servlet-mapping>
<url-pattern>/dwr/*</url-pattern>
</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()方法中已經初始化了。
public void handle(HttpServletRequest req, HttpServletResponse resp);
throws IOException
{
String pathinfo = req.getPathInfo();;
if(pathinfo == null || pathinfo.length(); == 0 || pathinfo.equals("/"););
{
resp.sendRedirect(req.getContextPath(); + req.getServletPath(); + '/' + "index.html");;
} else
if(pathinfo != null && pathinfo.equalsIgnoreCase("/index.html"););
doIndex(req, resp);;
if(pathinfo != null && pathinfo.startsWith("/test/"););
doTest(req, resp);;
if(pathinfo != null && pathinfo.equalsIgnoreCase("/engine.js"););
doFile(resp, "engine.js", "text/javascript");;
if(pathinfo != null && pathinfo.equalsIgnoreCase("/util.js"););
doFile(resp, "util.js", "text/javascript");;
if(pathinfo != null && pathinfo.equalsIgnoreCase("/deprecated.js"););
doFile(resp, "deprecated.js", "text/javascript");;
if(pathinfo != null && pathinfo.startsWith("/interface/"););
doInterface(req, resp);;
if(pathinfo != null && pathinfo.startsWith("/exec"););
doExec(req, resp);;
log.warn("Page not found. In debug");;
resp.sendError(404);;
}
}
哦。這些恍然大悟。"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">
nbsp; <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對象,并且及易擴充。
AjaxPro
1.GetStart
點選一個用戶端button,觸發一個javascript函數,執行一個隻有一個string參數的服務端方法,傳回一個處理過的string,處理方法是将傳入的string變成“Hi”+string +“!”,很簡單的一個例子。
伺服器端代碼如下:
1 AJAXDemo.Examples.Test
2 using System;
3 using AjaxPro;
4
5 namespace AJAXDemo.Examples.Test
6 {
7 public class TestMethod
8 {
9 public TestMethod()
10 {}
11
12 [AjaxMethod]
13 public string GetTest(string testText)
14 {
15 return "Hi," + testText + "!";
16 }
17 }
18 }
頁面代碼:
1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" >
4 <head runat="server">
5 <title>無标題頁</title>
6 <script type="text/javascript">
7
8 function doTest()
9 {
10 AJAXDemo.Examples.Test.TestMethod.GetTest("AjaxPro",doTest_callback);
11 }
12
13 function doTest_callback(res) {
14 alert(res.value);
15 }
16 </script>
17 </head>
18 <body>
19 <form id="form1" runat="server">
20 <div>
21 <input id="Button1" type="button" onclick="doTest()" value="測試"/></div>
22 </form>
23 </body>
24 </html>
2.分析
如果你已經成功運作,那麼檢視用戶端源檔案,你會發現多出下面的幾個腳本
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/prototype.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/core.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/converter.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx"></script>
通過使用http://localhost:3578/AJAXDemo.2/ajaxpro/prototype.ashx和http: //localhost:3578/AJAXDemo.2/ajaxpro/core.ashx不難發現,其中前面兩個是源代碼中帶的兩個js檔案(core.js和prototype.js)轉化出來的,
.net運作架構調用以下方法:
AjaxHandlerFactory.GetHandler
1.由于.ashx被注冊為自定義處理,此方法被調用4次;
2.依據請求類型(get)和請求的目标進行分别處理,分别對應EmbeddedJavaScriptHandler(2次),ConverterJavaScriptHandler(1次),TypeJavaScriptHandler(1次)
3..net運作架構在AjaxHandlerFactory.GetHandler傳回不同的IHttpHandler實作(EmbeddedJavaScriptHandler等)時調用該實作的以下方法:
EmbeddedJavaScriptHandler.ProcessRequest方法被調用:
ConverterJavaScriptHandler.ProcessRequest方法被調用:
TypeJavaScriptHandler.ProcessRequest方法被調用:
向用戶端輸出JavaScript 對象和方法的接口
基本内容也跟原來的檔案一樣,而converter.ashx和 AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx裡面有什麼呢?看下面代碼:
AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx
addNamespace("AJAXDemo.Examples.Test");
AJAXDemo.Examples.Test.TestMethod_class = Class.create();
AJAXDemo.Examples.Test.TestMethod_class.prototype = (new AjaxPro.AjaxClass()).extend({
GetTest: function(testText) {
return this.invoke("GetTest", {"testText":testText}, this.GetTest.getArguments().slice(1));
},
initialize: function() {
this.url = '/AJAXDemo.2/ajaxpro/AJAXDemo.Examples.Test.TestMethod,App_Code.un7rskvh.ashx';
});
AJAXDemo.Examples.Test.TestMethod = new AJAXDemo.Examples.Test.TestMethod_class();
converter.ashx
addNamespace("Ajax.Web");
Ajax.Web.NameValueCollection = function()
{
this.__type = "System.Collections.Specialized.NameValueCollection";
this.add = function(key, value) {
if(this[key] == null) {
this[key] = value;
this.getKeys = function() {
var keys = ;
for(key in this)
if(typeof this[key] != "function")
keys.push(key);
return keys;
this.getValue = function(key) {
return this[key];
this.toJSON = function() {
var o = this;
o.toJSON = null;
delete o.toJSON;
return AjaxPro.toJSON(o);
}
//當使用者觸發表現層控件動作事件
1.由于生成的用戶端腳本中包含對于"/AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx"的調用,導緻AjaxHandlerFactory.GetHandler被調用
2.在AjaxHandlerFactory.GetHandler中傳回AjaxSyncHttpHandler
3..net運作架構在AjaxHandlerFactory.GetHandler傳回不同的IHttpHandler實作(AjaxSyncHttpHandler等)時調用該實作的以下方法:
AjaxSyncHttpHandler.ProcessRequest方法被調用:
将調用轉嫁到AjaxProcHelper.Run操作:
1.通過反射實作對指定C#類方法的調用.
2.将反射調用結果通過XmlHttpRequestProcessor.SerializeObject中對JavaScriptSerializer.Serialize的調用,通過Response向用戶端傳回.
研究完AjaxPro 和DWR 的機制,個人總結了一些比較;
1.AjaxPro 面向對背景方法的通路,針對的是帶有Public,Private的方法,而不是具體類,DWR中沒有這樣的限制,它是對JAVA類的整體反射,對所有方法生成javascript 接口方法,當然,你也可以選擇繼承它的反射類,按自己的需求自定義一個代理接口供DWR來反射,畢竟,這是開源的一大優勢。但總的來說,AjaxPro比DWR考慮的更安全,更精細。
2.DWR中繼承了Spring的思想,靈活運用Reflect和配置檔案,使應用程式的擴充性更強,更靈活。
3.AjaxPro 針對某個具體的業務實作需要進行背景的配置,如加入【AjaxPro.Method】,為每個Code Behind 寫入一個頁面注冊事件
AjaxPro.Utility.RegisterTypeForAjax(typeof(_Default)); //_Default 為頁面的背景代碼類
試想一下,如果你有很多這樣的Ajax業務應用,而每個頁面都需要這樣注冊,是不是很繁瑣?交叉業務的維護呢……
而DWR将一個業務應用的JAVA Class 轉換成JavaScript,不需要對JAVA Model 有任何操作,隻需在配置檔案dwr.xml中create.簡單,便于維護,特别是在大的項目中跟能展現。