天天看點

AJaxPro 與 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

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到底是如何工作滴。

AJaxPro 與 DWR 的研究與比較
AJaxPro 與 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;&nbsp;&nbsp;&nbsp;&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

&nbsp;&nbsp;&nbsp; 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 +“!”,很簡單的一個例子。

伺服器端代碼如下:

AJaxPro 與 DWR 的研究與比較
AJaxPro 與 DWR 的研究與比較

 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 }

頁面代碼:

AJaxPro 與 DWR 的研究與比較
AJaxPro 與 DWR 的研究與比較

 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裡面有什麼呢?看下面代碼:

AJaxPro 與 DWR 的研究與比較
AJaxPro 與 DWR 的研究與比較

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.簡單,便于維護,特别是在大的項目中跟能展現。