DWR學習詳解

DWR的原理爲:把JAVA類注入JS從且達到可以在JS中調用JAVA類中的方法,實現了異步調用。
同GWT相比DWR的優點有
一、DWR可以同JS和HTML同步調用,從且解決GWT中頁面美工排版的困難。
二、dwr是把JAVA類注入JS中,而不需要學習新的技術。

web.xml文件配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoting.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
</web-app>
dwr文件配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://www.getahead.ltd.uk/dwr/dwr20.dtd">
<dwr>
<allow>
<create creator="new" javascript="service">
<param name="class" value="cn.epem.admin.Service" />
</create>
</allow>
</dwr>

test.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<html>
<head>
<title>My JSP 'first_dwr.jsp' starting page</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/loadreport.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/dwr/engine.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/dwr/util.js"></script>
<script type='text/javascript' src='<%=request.getContextPath()%>/dwr/interface/service.js'> </script>

</head>
<body>
<input type="button" name="button" value="測試" onclick="firstDwr()">
</body>
</html>


loadreport.js

function firstDwr(){
service.sayHello("Jorwen",callBackHello);
}
function callBackHello(data){
alert(data);
}


Service.java

package cn.epem.admin;

public class Service {
public String sayHello(String yourName) {
System.out.println(yourName);
return "Hello World " + yourName;
}
}



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


java代碼:

web.xml配置
<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>



這樣所有的/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()方法中已經初始化了。

java代碼:

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);
} else
if(pathinfo != null && pathinfo.startsWith("/test/"))
{
doTest(req, resp);
} else
if(pathinfo != null && pathinfo.equalsIgnoreCase("/engine.js"))
{
doFile(resp, "engine.js", "text/javascript");
} else
if(pathinfo != null && pathinfo.equalsIgnoreCase("/util.js"))
{
doFile(resp, "util.js", "text/javascript");
} else
if(pathinfo != null && pathinfo.equalsIgnoreCase("/deprecated.js"))
{
doFile(resp, "deprecated.js", "text/javascript");
} else
if(pathinfo != null && pathinfo.startsWith("/interface/"))
{
doInterface(req, resp);
} else
if(pathinfo != null && pathinfo.startsWith("/exec"))
{
doExec(req, resp);
} else
{
log.warn("Page not found. In debug/test mode try viewing /[WEB-APP]/dwr/");
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">
<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對象,並且及易擴展。


1 dwr支持的java類型於javascript類型如何轉化.
2 dwr配置說明.
3 於spring,struts,webwork的集成.
4 dwr是如何解決我的那些疑問的.
http://forum.iteye.com/viewtopic.php?t=14083
5 見 http://forum.iteye.com/viewtopic.php?t=13995.現在我自己傾向於用javascript代替xml.畢竟瀏覽器消費最好是javascript對象.


1、最小配置

java代碼:

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



2、當我們想看DWR自動生成的測試頁(Using debug/test mode)時,可在
java代碼:

servlet中加
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>


這個參數DWR默認是false.如果選擇true.我們可以通過url http://localhost:port/app/dwr ,你就可以看到你部署的每個DWR class。並且可以測試java代碼的每個方法是否運行正常。爲了安全考慮,在正式環境下你一定把這個參數設爲false.
3、logging信息配置。
在無java.util.logging的jdk1.3下運行DWR,我們不希望強制用戶加一個logging包,而是用HttpServlet.log ()方法來輸出日誌。如果classpath中包括logging jar包,DWR自動切換用logging輸出日誌。
如果用HttpServlet.log()方法,以下配置是有效的。

java代碼:

<init-param>
<param-name>logLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>


有效的值是 FATAL, ERROR, WARN (the default), INFO and DEBUG

我是喜歡用log4j輸出日誌,那麼在log4j.properties下加,log4j.logger.uk.ltd.getahead.dwr = debug。這樣可以看DWR的調試日誌。
4、多dwr.xml文件的配置
可能有幾種情況,我們一一列舉。 一個servlet,多個dwr.xml配置文件;多個servlet,每個servlet對應一個或多個dwr.xml.
一個servlet,多個dwr.xml配置文件;

java代碼:

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>config-1</param-name>
<param-value>WEB-INF/dwr1.xml</param-value>
</init-param>
<init-param>
<param-name>config-2</param-name>
<param-value>WEB-INF/dwr2.xml</param-value>
</init-param>
</servlet>


在這種配置下,param-name的值必須以config開頭。param-name可以有>= 0個。如果沒有param-name,那麼將會讀取WEB-INF/dwr.xml。如果有大於零個param-name,那麼WEB- INF/dwr.xml文件將不會被讀取。

多個servlet,每個servlet對應一個或多個dwr.xml

java代碼:

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<!--用classes/dwr.xml-->
</servlet>
<servlet>
<servlet-name>dwr-invoker1</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>config-admin</param-name>
<param-value>WEB-INF/dwr1.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr-invoker1</servlet-name>
<url-pattern>/dwr1/*</url-pattern>
</servlet-mapping>


在這種情況下,我們可以根據J2EE security來控制權限,針對不同url,加不同的角色。
5、dwr的幾個擴展點(Plug-ins)
DWR對以下接口提供的默認的實現,用戶可以繼承DWR的默認實現類來達到我們想要的效果。但這至少需要我們讀了DWR源碼才能做這些工作(dwr源碼很是清晰,有興趣可以學習一下),以後可能補存這部分。

java代碼:

uk.ltd.getahead.dwr.AccessControl
uk.ltd.getahead.dwr.Configuration
uk.ltd.getahead.dwr.ConverterManager
uk.ltd.getahead.dwr.CreatorManager
uk.ltd.getahead.dwr.Processor
uk.ltd.getahead.dwr.ExecutionContext


一點感想:

1、java的方法避免用 java 和 javascript的關鍵字。 如 try()方法 或 delete()方法。
2、在你的java類中不要出現重載 avoid overloaded methods.
3、在生產環境下,你可以把deprecated.js engine.js util.js 合併成一個,並放到web容器內,直接在htm,jsp中引用。 (對於java代碼轉換成的javascript代碼也可以這麼做,免得每次生成javascript代碼浪費時間)
4、研究一下Bean Converters。POJOjava對象必須嚴格按照 getProperty() setProperty()定義。Bean Converters是按照 java對象的 get set方法工作的。DWR1.1加了Object Converters,是按private int property;這類屬性工作的。
<convert converter="bean" match="example.Fred"/>
<param name="exclude" value="property1, property2"/>
</convert>
當你這樣配置時,java對象轉化成的javascript對象不包括property1和property2。
<convert converter="bean" match="example.Fred"/>
<param name="include" value="property1, property2"/>
</convert>
反過來java對象轉化成的javascript對象直包括property1和property2。
5、<convert converter="collection" match="java.util.Collection"/>
<convert converter="map" match="java.util.Map"/>
這也是容易出問題的地方.

package example;
public class Person{
private String name;
get ... set
private List relationPerson;
public List setRelationPerson(List relationPerson){
this.relationPerson=relationPerson;
}
}
<convert converter="bean" match="example.Person"/>
</convert>
<signatures>
<![CDATA[
import java.util.Set;
import example.Person;

Person.setRelationPerson(List<Person>);
]]>
</signatures>

在jdk1.5泛型中有寫法區別


6、另外在Set,List等做爲方法參數時也會出現混淆。返回集合類型不會出現問題,想想就知道了。
如 在Test類中有 public Set testBeanSetParam(Set test) 這個方法,客戶端得到的javascript方法可能是Test.testBeanSetParam(p0,callback);當我們 javascript調用這個方法時,鬼才知道怎麼確定p0的類型,也不可能知道Set集合中該放什麼類型的java對象,所以dwr的 special signatures syntax 確定這些集合和內容的類型
<signatures>
<![CDATA[
import java.util.Set;
import example.Test;

Test.testBeanSetParam(Set<TestBean>);
<!--Test.stringStringMapParam(Map<String, String>);-->
]]>
</signatures>
7、Creators
<allow>
<create creator="..." javascript="..." scope="...">
<param name="..." value="..."/>
<auth method="..." role="..."/>
<exclude method="..."/>
<include method="..."/>
</create>
...
</allow>
爲了更少的暴露業務方法,最好配置include屬性。
dwr支持new ,script,struts....幾種集成方法,也支持static方法的調用,我覺得最好的是spring,其他感覺是處理遺留問題處理。
8、engine_js 作爲dwr框架客戶端核心,主要完成xmlHttp或iframe的構造,我們沒必要關心它如何實現。有幾點創新的我們可以學習下。
Call Batching 我們可以把幾個客戶端請求一起放送到服務器端,減少了網絡交互,但要注意依存關係和他們處理的順序。
Call Ordering 同步異步調整。一般用默認的就好了。注意依存關係。
Remoting Hooks 鉤子,"small AOP"
依存關係解釋。 如果 request1() request2()兩個業務邏輯方法,request2方法需要用到request1方法從服務器端返回的結果。如果調用request2時,request1還沒處理或還沒請求。 下拉框連動可能有這個問題。
9、util.js propotype.js有些重複,這讓我很難受。只能改代碼了,可別壞了開元協議。
10、如果你的回調方法想加其他參數
var dataFromBrowser = ...;
var callbackProxy = function(dataFromServer) {
callbackFunc(dataFromServer, dataFromBrowser);
};
var callMetaData = { callback:callbackProxy };
Remote.method(params, callMetaData);
11、dwr1.1
1.1只能算一個bug消除版本,沒有什麼大的功能調整。源代碼結構做了些調整。2.0有新的特徵加入。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章