??xml version="1.0" encoding="utf-8" standalone="yes"?>
<head>
<title>xmlhttprequest ajax demo</title>
<script type ="text/javascript" language ="javascript" >
var req; //定义变量Q用来创建xmlhttprequest对象
function creatReq() // 创徏xmlhttprequest,ajax开?br />
{
var url="ajaxServer.aspx"; //要请求的服务端地址
if(window.XMLHttpRequest) //非IE览器及IE7(7.0及以上版?Q用xmlhttprequest对象创徏
{
req=new XMLHttpRequest();
}
else if(window.ActiveXObject) //IE(6.0及以下版?览器用activexobject对象创徏,如果用户览器禁用了ActiveX,可能会失? {
req=new ActiveXObject("Microsoft.XMLHttp");
}
if(req) //成功创徏xmlhttprequest
{
req.open("GET",url,true); //与服务端建立q接(h方式post或getQ地址,true表示异步)
req.onreadystatechange = callback; //指定回调函数
req.send(null); //发送请?br />
}
}
function callback() //回调函数Q对服务端的响应处理Q监视response状?br />
{
if(req.readystate==4) //h状态ؓ4表示成功
{
if(req.status==200) //http状?00表示OK
{
Dispaly(); //所有状态成功,执行此函敎ͼ昄数据
}
else //httpq回状态失?br />
{
alert("服务端返回状? + req.statusText);
}
}
else //h状态还没有成功Q页面等?br />
{
document .getElementById ("myTime").innerHTML ="数据加蝲?;
}
}
function Dispaly() //接受服务端返回的数据Q对其进行显C?br />
{
document .getElementById ("myTime").innerHTML =req.responseText;
}
</script>
</head>
<body>
<div id="myTime"></div>
<input id="Button1" type="button" value="Get Time" onclick ="creatReq();"/>
</body>
</html>
对于Ajax需要注意执??序
对于input cd为text?动作?onChange() onPropertyChange() 后者比前者的反应 更加的敏感,有可能onChange()不会随着里面的改变而改变,但是onPropertyChange()肯定会随着value的?改变而改变?
对于q回的|要把l果写在最后的条g都符合的情况下,是注意函数的位|?
操作xml的时?用到的方?和以前操作xml的是一LQ如getElementsByTagName(),q有.firstChild.data,nodeValue.
Ajax执行的顺序是open,onreadystatechange,send 都是Z的形式传输?/p>
对于servlet里面 注意 都是 用流的Ş式进?传输和取倹{resp.setContentType()Q里面得相应的设|方式?/p>
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/zuowangxi/archive/2009/12/30/5104036.aspx
![]() |
U别Q?中 ??/a> (zhouting@cn.ibm.com), 软g工程? IBM 中国软g开发技术实验室 2007 q?8 ?31 ?/p> 很多应用譬如监控、即旉信、即时报Ll都需要将后台发生的变化实时传送到客户端而无dL不停地刷新、发送请求。本文首先介l、比较了常用?#8220;服务器推”ҎQ着重介l了 Comet Q?使用 HTTP 长连接、无L览器安装插g的两U?#8220;服务器推”ҎQ基?AJAX 的长轮询方式Q基?iframe ?htmlfile 的流方式。最后分析了开?Comet 应用需要注意的一些问题,以及如何借助开源的 Comet 框架Qpushlet 构徏自己?#8220;服务器推”应用?/blockquote> |
<title><fmt:message key="menu.admin" />-><fmt:message key="menu.admin.departmentsManager" /></title>
<link rel="stylesheet" type="text/css" media="all" href="<c:url value='/styles/default.css'/>" />
<link rel="stylesheet" type="text/css" media="all" href="<c:url value='/styles/helptip.css'/>" />
<link rel="stylesheet" type="text/css" media="print" href="<c:url value='/styles/print.css'/>" />
<c:if test="${not empty errorReason}">
<div align="center" style="color: red">
<c:if test="${errorReason=='updateConstraint'}">
<fmt:message key="tcsBox.updateConstraint"/>
</c:if>
<c:if test="${errorReason=='addConstraint'}">
<fmt:message key="tcsBox.addConstraint"/>
</c:if>
</div>
</c:if>
<html:form action="editBoxes.html?method=finishEdit" styleId="TcsboxForm" >
<table class="detail" valign="top">
<c:if test="${from == 'update' or param.handle =='update'}">
<input type="hidden" value="update" name="handle"/>
</c:if >
<c:if test="${from == 'add'}">
<input type="hidden" value="add" name="handle"/>
</c:if >
<!-- <c:set var="pageButtons">
<tr>
<td height="50"></td>
<td class="buttonBar">
<c:if test="${from == 'update'}">
<input type="submit" value='<fmt:message key="button.submit"/>' class="button" />
<input type="hidden" value="update" name="handle"/>
</c:if >
<c:if test="${from == 'add'}">
<input type="submit" value='<fmt:message key="button.submit"/>' class="button" />
<input type="hidden" value="add" name="handle"/>
</c:if >
<input type="reset" value='<fmt:message key="button.reset"/>' class="button"/>
<input type="button" value='<fmt:message key="button.back"/>' class="button" onclick="history.back()"/>
</td>
</tr>
</c:set> -->
<c:if test="${from=='update' or param.handle =='update'}">
<tr>
<th>
<manhourpool:label key="TcsBoxForm.tcs_box_id"/>
</th>
<td>
<html:text property="tcs_box_id" styleId="tcs_box_id" readonly="true"></html:text>
</td>
</tr>
</c:if>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.code"/>
</th>
<td>
<html:text property="tcs_box_code" styleId="tcs_box_code" ></html:text>
</td>
</tr>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.tcs_box_name"/>
</th>
<td>
<html:text property="tcs_box_name" styleId="tcs_box_name" ></html:text>
</td>
</tr>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.companycode"/>
</th>
<td>
<html:select property="company_code" styleId="company_code" onchange="changeCompany_Code(this)">
<html:options collection="companyLst" property="code" labelProperty="name" />
</html:select>
</td>
</tr>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.burea"/>
</th>
<td>
<html:select property="bureau_id" styleId="bureau_id" >
<html:options collection="bureauLst" property="code" labelProperty="name" />
</html:select>
</td>
</tr>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.district"/>
</th>
<td>
<html:select property="district_id" styleId="district_id" >
<html:options collection="districtLst" property="code" labelProperty="name" />
</html:select>
</td>
</tr>
<tr>
<th>
<manhourpool:label key="TcsBoxForm.address"/>
</th>
<td>
<html:text property="tcs_address" styleId="tcs_address" ></html:text>
</td>
</tr>
<c:if test="${from=='update' or param.handle =='update'}">
<tr>
<th>
<manhourpool:label key="chartem.state"/>
</th>
<td>
<html:select property="state" >
<html:options collection="doms" property="column_value" labelProperty="description"/>
</html:select>
</td>
</tr>
</c:if>
</table>
<table align="center">
<!-- <c:out value="${pageButtons}" escapeXml="false"/> --></table>
</html:form>
<script type='text/javascript' src='dwr/interface/bureauManager.js'></script>
<script type='text/javascript' src='dwr/interface/districtManager.js'></script>
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
<script type="text/javascript">
var bureau_on_load=true;
var bureau_id='<c:out value="${tcsBoxForm.bureau_id}"/>';
window.onload=function(){
window.changeCompany_Code(document.all.company_code);
var ppage = window.parent.document.frames['theiframtwo'];
if('<c:out value="${handlesucc}" />'){
ppage.document.getElementById('viewBoxes').submit();
}
}
function changeCompany_Code(sel_obj)
{
var company_code=sel_obj.value;
if(company_code=='')
return;
var param =new Object();
param.company_code=company_code;
bureauManager.getBureauListInDwr(param,showBureau);
districtManager.getDistrictBycompany(company_code,showDistrict);
}
function showBureau(bureaus)
{
// var bureau_id=$('bureau_id').value;
// if(window.bureau_on_load)
// {
// bureau_id='<c:out value="${param.bureau_id}"/>';
// window.bureau_on_load=false;
// }
DWRUtil.removeAllOptions('bureau_id');
var op=document.createElement('option');
op.value='';
op.text='<fmt:message key="task.default" />';
$('bureau_id').options.add(op);
DWRUtil.addOptions('bureau_id',bureaus,'bureau_id','bureau_name');
for(var i=0;i<$('bureau_id').options.length;i++)
{
if($('bureau_id').options[i].value==bureau_id)
{
$('bureau_id').selectedIndex=i;
break;
}
}
}
function showDistrict(dis)
{
var dis_code=$('district_id').value;
if(window.isOnload)
{
dis_code='<c:out value="${taskInfoForm.district_id}"/>';
window.isOnload=false;
}
DWRUtil.removeAllOptions('district_id');
var op=document.createElement('option');
op.value='';
op.text='<fmt:message key="task.default" />';
$('district_id').options.add(op);
DWRUtil.addOptions('district_id',dis,'code','name');
for(var i=0;i<$('district_id').options.length;i++)
{
if($('district_id').options[i].value==dis_code)
{
$('district_id').selectedIndex=i;
break;
}
}
}
</script>
</td>
</tr>
<tr>
<th>
<fmt:message key="timelimitmv.value.name"/>
</th>
<td>
<select name="begin_value" id="begin_value">
<option value="1" ><fmt:message key="timelimitmv.type1"/></option>
<option value="2"><fmt:message key="timelimitmv.type2"/></option>
<option value="4"><fmt:message key="timelimitmv.type4"/></option>
</select>
</td>
</tr>
DWR.xml配置文g说明?doc
DWRW记.doc
DWR技术分?doc
DWR开发培?ppt
DWR学习.doc
打包下蝲 http://www.javaeye.com/topic/32782
DWR学习 http://www.javaeye.com/topic/16424
DWR应用ȝ http://www.javaeye.com/topic/33678
Message物g如下…
1234567891011121314151617181920212223 package onlyfun.caterpillar; public class Message { private long id = System.currentTimeMillis(); private String text; public Message(String newtext) { text = newtext; if (text.length() > 256) { text = text.substring(0, 256); } text = text.replace('<', '['); text = text.replace('&', '_'); } public long getId() { return id; } public String getText() { return text; }}
Servlet接受a息新增與查詢,判斷的方式是檢查請求參數task是send或query?
Servlet會以XML傛_目前List當中的訊息,客戶端可以查詢或插入新訊息時Q取得目前List中的a息Q接著在web.xml中設定一?#8230;
12345678910111213141516171819202122 <?xml version="1.0" encoding="UTF-8"?><web-app id="WebApp_ID" 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> <description> </description> <display-name> ChatRoomServlet</display-name> <servlet-name>ChatRoomServlet</servlet-name> <servlet-class> onlyfun.caterpillar.ChatRoomServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ChatRoomServlet</servlet-name> <url-pattern>/ChatRoomServlet</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> </web-app>
在網頁中Q用者可以在輸入a息後按下按鈕送出資訊Q並在XML回應取得時,訊息以一列一列的表格方式示ZQ另外還a定了週期性的輪詢Q即使不輸入新訊息,也可以週期性的取得新的聊天a息…
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"><title>Chat Room</title> <script type="text/javascript">var xmlHttp; function createXMLHttpRequest() { if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); }} function sendMessage() { var msg = document.getElementById("text").value; if(msg == "") { refreshMessage(); return; } var param = "task=send&msg=" + msg; ajaxRequest(param); document.getElementById("text").value = "";} function queryMessage() { var param = "task=query"; ajaxRequest(param);} function ajaxRequest(param) { var url = "ChatRoomServlet?timestamp" + new Date().getTime(); createXMLHttpRequest(); xmlHttp.onreadystatechange = refreshMessage; xmlHttp.open("POST", url, true); xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); xmlHttp.send(param);} function refreshMessage() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var messages = xmlHttp.responseXML.getElementsByTagName("message"); var table_body = document.getElementById("dynamicUpdateArea"); var length = table_body.childNodes.length; for (var i = 0; i < length; i++) { table_body.removeChild(table_body.childNodes[0]); } var length = messages.length; for(var i = length - 1; i >= 0 ; i--) { var message = messages[i].firstChild.data; var row = createRow(message); table_body.appendChild(row); } setTimeout("queryMessage()", 2000); } }} function createRow(message) { var row = document.createElement("tr"); var cell = document.createElement("td"); var cell_data = document.createTextNode(message); cell.appendChild(cell_data); row.appendChild(cell); return row;} </script> </head><body> <p> Your Message: <input id="text"/> <input type="button" value="Send" onclick="sendMessage()"/></p> <p>Messages:</p> <table align="left"> <tbody id="dynamicUpdateArea"></tbody> </table> </body></html>
單抓個畫?#8230;
直接用AJAXQ後端用JSP/ServletQ您要對請求參數做些判斷Q看看是新增a息或查詢,並要自行輸出XMLQ有的沒?#8230;
ҎDWR的話Q就很簡單了Q寫個簡單的Java物g…
1234567891011121314151617181920212223 package onlyfun.caterpillar; import java.util.LinkedList;import java.util.List; public class Chat { private static LinkedList<Message> messages = new LinkedList<Message>(); public List addMessage(String text) { if (text != null && text.trim().length() > 0) { messages.addFirst(new Message(text)); while (messages.size() > 10) { messages.removeLast(); } } return messages; } public List getMessages() { return messages; }}
接著…在dwr.xml中開放一?#8230;
12345678910111213 <?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="Chat"> <param name="class" value="onlyfun.caterpillar.Chat"/> </create> <convert converter="bean" match="onlyfun.caterpillar.Message"/> </allow></dwr>
使用者取得訊息時Q是直接傛_List物gQ而List中裝的是Message物gQMessage物g是自a物Ӟcontervera定?beanQ表CDWR會自動將Message的getter名稱轉換為傳回客戶端的JavaScript物g中的屬性,例如Message中有 getText()Q則在客戶端可以用message.text這樣的方式來存取?
接著是簡單的客戶端網?#8230;
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"><title>Insert title here</title> <script src="dwr/interface/Chat.js" type="text/javascript"></script><script src="dwr/engine.js" type="text/javascript"></script><script src="dwr/util.js" type="text/javascript"></script> <script type="text/javascript">function sendMessage() { var text = DWRUtil.getValue("text"); DWRUtil.setValue("text", ""); Chat.addMessage(text, gotMessages);} function gotMessages(messages) { var chatlog = ""; for (var data in messages) { chatlog = "<div>" + messages[data].text + "</div>" + chatlog; } DWRUtil.setValue("chatlog", chatlog); setTimeout("queryMessage()", 2000);} function queryMessage() { Chat.getMessages(gotMessages);}</script> </head><body> <p> Your Message: <input id="text"/> <input type="button" value="Send" onclick="sendMessage()"/></p> <p>Messages:</p><div id="chatlog"></div> </body></html>
當List物g傛_時,它成為gotMessages(messages)中的messages物gQ而messages物g中包?Message物g轉換後對應的JavaScript物gQ由於我們已E設定了ConverterQ所以可以用messages[data].text?取得傛_的訊?#8230;
單抓個畫?#8230;
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/caterpillar_here/archive/2006/09/30/1311605.aspx
例如一個示意的JavaE式如下Q?
12345678 package onlyfun.caterpillar; public class Option { public String[] getOptions() { // 實際上這些字串是從資料庫中查到的啦… return new String[] {"良葛?, "毛美?, "c_?}; }}
傛_的字串陣列,您要填寫C拉選單中Q當Ӟ首先我們要在dwr.xml中開發這個物?#8230;
12345678910 <?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="OPT"> <param name="class" value="onlyfun.caterpillar.Option"/> </create> </allow></dwr>
這是我們的E頁…
123456789101112131415 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"><script src="option.js" type="text/javascript"></script><script src="dwr/interface/OPT.js" type="text/javascript"></script><script src="dwr/engine.js" type="text/javascript"></script><script src="dwr/util.js" type="text/javascript"></script> </head> <body> 達R: <select id="opts"></select></body></html>
傛_的字串陣列會填入opts這個select中,我們的option.js如下…
12345678 window.onload = function() { OPT.getOptions(populate); }; function populate(list){ DWRUtil.removeAllOptions("opts"); DWRUtil.addOptions("opts", list);}
夠簡單了…不需要解釋了…
看一下結?#8230;
好啦Q我知道有h在說了,這個程式有夠無?#8230;
改一下!是個不錯的例了,例如連動方塊Q唔Q在Ajax in action中叫啥?Dynamic double comboQ?#8230;
假設一個會d資料庫中查詢資料的JavaE式C意如下Q?
123456789101112131415161718192021222324252627282930 package onlyfun.caterpillar; import java.util.Map;import java.util.TreeMap; public class Bike { private Map<String, String[]> bikes; public Bike() { bikes = new TreeMap<String, String[]>(); bikes.put("2000", new String[] {"2000 T1", "2000 T2", "2000 T3"}); bikes.put("2001", new String[] {"2001 A1", "2001 A2"}); bikes.put("2002", new String[] {"2002 BW1", "2002 BW2", "2002 BW"}); bikes.put("2003", new String[] {"2003 S320"}); bikes.put("2004", new String[] {"2004 TA1", "2004 TA2", "2004 TA3"}); } public String[] getYears() { String[] keys = new String[bikes.size()]; int i = 0; for(String key : bikes.keySet()) { keys[i++] = key; } return keys; } public String[] getBikes(String year) { return bikes.get(year); }}
getYears()跟getBkies()分別表示產品的年份跟型號Q這邊用Map模擬Q實際上資料是來自資料n的查詢?
一樣的Q在dwr.xml中設定:
12345678910 <?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="Bike" scope="application"> <param name="class" value="onlyfun.caterpillar.Bike"/> </create> </allow></dwr>
我們會有個腳t車q䆾與型號查詢頁面:
123456789101112131415 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"><title>Insert title here</title> <script type='text/javascript' src='dwr/interface/Bike.js'></script> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript' src='bike.js'></script></head><body onload="refreshYearList();"> q䆾Q?lt;select id="years" onchange="refreshBikeList();"></select><br/><br/> 型號Q?lt;select id="bikes"></select><br/></body></html>
注意Q在選完W一個年份後Q會觸發onchange事gQ接著第二個下拉選單會自動填上應q䆾的型號,而不是按鈕按下,再去取得W二個下拉選單,然後refresh...blah...blah...
bike.js如下…
12345678910111213141516171819 function refreshYearList() { Bike.getYears(populateYearList);} function populateYearList(list){ DWRUtil.removeAllOptions("years"); DWRUtil.addOptions("years", list); refreshBikeList();} function refreshBikeList() { var year = $("years").value; Bike.getBikes(year, populateBikeList);} function populateBikeList(list){ DWRUtil.removeAllOptions("bikes"); DWRUtil.addOptions("bikes", list);}
一樣很單…
看個無聊的畫面…XD
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/caterpillar_here/archive/2006/09/18/1239538.aspx
請先?http://getahead.ltd.uk/dwr/ 下載 dwr.jarQ放到WEB-INF/lib?#8230;
負K處理客戶端請求,並呼叫Java物g的是DWRServletQDWR其實也有些Model 2的味道,只是View的這一層比較弱Q因為放到客戶端的JavaScript應用E式?#8230;
在web.xml中加入DWRServlet…
1234567891011121314151617181920212223 <?xml version="1.0" encoding="UTF-8"?><web-app id="WebApp_ID" 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"> <display-name> ajaxDWR</display-name> <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <description> </description> <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>
接下來寫個簡單的Hello吧!
1234567 package onlyfun.caterpillar; public class Hello { public String hello(String name) { return "哈囉Q? + name + "Q您的第一個DWRQ?; }}
客戶端要呼叫這個Java物gQ傳i它參數Q而後傛_一個字Ԍ客戶端再示這個字Ԍ奇Q其實是要告aDWRServlet這g事,這需要一個dwr.xmlQ?
1234567891011 <?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="Hello"> <param name="class" value="onlyfun.caterpillar.Hello" /> </create> </allow></dwr>
creatora定為newQ表CZ用Hello的無參數建構子來生成物gQjavascripta定為HelloQ表C客戶端JavaScriptE式可以使用Hello來呼叫對應的onlyfun.caterpillar.Hello物g?
來寫個客戶端的網頁,當中有一個入欄?#8230;
12345678910111213141516171819 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"> <title>W一個DWRE式</title> <script type='text/javascript' src='dwr/interface/Hello.js'></script> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript' src='hello.js'></script></head><body> <input id="user" type="text" /><input type='button' value='哈囉' onclick='hello();' /> <div id="result"></div> </body></html>
dwr/interface/Hello.js是由DWRServletҎdwr.xml中的a定生成的,engine.js負K客戶端伺服端溝通,util.js是一些好用的JavaScriptE式Q可以讓您少寫很多JavaScript?
hello.js是我們自a的函式Q按下按鈕後Q會呼叫當中的hello()函式Q?
12345678 function hello() { var user = $('user').value; Hello.hello(user, callback);} function callback(msg) { DWRUtil.setValue('result', msg);}
${'user'}取得輸入Ƅ位的DOM物gQvalue取得當中的欄位|而後呼叫Hello.hello()Q並value當作參數傳?#8230; i果是呼叫Server端的Hello Java物gQ當i果傛_後,會呼叫JavaScript的callback函式QDWRUtil的setValue()Ҏ會將傛_的msga定i指?id的DOMQ結果就?#8230;啥!AJAX的功能在?#8230;這個而言是發出非同步請求,而回應不用Refresh頁面啦!
好啦Q這個無聊的Hello DWR可以做啥Q?#8230;XD
已經可以讓您做個簡單的文字提示功能?#8230;像這?#8230;
http://caterpillar.onlyfun.net/Gossip/index.html
把滑鼠指到書的照片上Q會示提示文字Q這些提示文字本n不是存在E頁上的Q而是在Server端,當滑鼠指到書上時Q會用Request objectLQ然後顯C在框框?#8230;
當然Q我的網站只支援PHPQ所以那不是DWR完成的功能,而且我是直接用Request object跟DOML慢刻?#8230;初學者來說已E有些麻煩了…XD
不過Q用DWR可以很單完成這個功?#8230;
先寫個Java別吧!會抓properties檔案中的文字a息Q例?#8230;
123456789101112131415 package onlyfun.caterpillar; import java.util.ResourceBundle; public class Book { private ResourceBundle resource; public Book() { resource = ResourceBundle.getBundle("book"); } public String getDescription(String key) { return resource.getString(key); }}
從程式中q道,它會Lbook_zh_TW.properties的資料,這不是重點啦Q只是Java的一個功能,我們要看的是DWRQ不過先把book_zh_TW.properties準備?#8230;
123 java=Java 學習{記的介?… BlaBla...spring=Spring 技術手冊的介紹…BlaBla...ajax=Ajax in action 中文版的介紹…
唔!裏頭是中文字Q自qnative2ascii轉換?#8230;這也不是重點…我們是要看DWR怎麼做到文字提示功能…
一樣的…要開N個Book物gQ在dwr.xml?#8230;
1234567891011 <?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="Book" scope="application"> <param name="class" value="onlyfun.caterpillar.Book"/> </create> </allow></dwr>
scopea定為applicationQ表C這個Book物g在整個應用程式階D都z著?
然後Q客戶端寫個網?#8230;
12345678910111213141516171819202122232425262728293031323334353637383940 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=BIG5"> <script type='text/javascript' src='dwr/interface/Book.js'></script> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript' src='book.js'></script><title>個h著/譯作</title></head><body> <div id="ajax" onmouseover="getBookData(this);" onmouseout="clearData();"><a ><small><img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Ajax in action 中文? title="Ajax in action 中文? src="images/ajax_in_action_c.jpg" hspace="10" vspace="2"></small></a></div> <div id="spring" onmouseover="getBookData(this);" onmouseout="clearData();"><a ><small><img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Spring 技術手? title="Spring 技術手? src="images/SpringTech_S.jpg" hspace="10" vspace="2"></small></a></div> <div id="java" onmouseover="getBookData(this);" onmouseout="clearData();"><a ><small><img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Java 學習{記" title="Java 學習{記" src="images/JavaGossip_Cover_Small.jpg" hspace="10" vspace="2"></small></a></div> <br/><br/><br/><br/><br/><br/> <div id="info"></div> </body></html>
重點在於onmouseover跟onmouseoutQ滑鼠移入與Ud時會呼叫的函式,還有最下面的infoQ抓回來的書c介Ҏ攑ֈ當中…
book.js如下Q簡單的?#8230;
1234567891011 function getBookData(ele) { Book.getDescription(ele.id, setBookData);} function setBookData(description) { DWRUtil.setValue('info', description);} function clearData() { DWRUtil.setValue('info', '');}
E式很簡單,我懶得解釋了…XD
看一下畫面好?#8230;這是滑鼠Ud Ajax in action中文?上的介紹畫面…
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/caterpillar_here/archive/2006/09/06/1186566.aspx
作ؓ一U广泛 用的 Web 应用E序开发技术,Ajax 牢固立了自qCQ随之而来的是一些通用 Ajax 使用模式。例如,Ajax l常用于对用戯入作出响应,然后使用从服务器获得的新数据修改面的部分内宏V但是,有时 Web 应用E序的用L面需要进行更C响应服务器端发生的异步事Ӟ而不需要用h?—?例如Q显C到?Ajax 聊天应用E序的新消息Q或者在文本~辑器中昄来自另一个用L改变。由于只能由览器徏?Web 览器和服务器之间的 HTTP q接Q服务器无法在改动发生时变?“推?#8221; l浏览器?/p>
Ajax 应用E序可以使用两种基本的方法解册一问题Q一U方法是览器每隔若q秒旉向服务器发出轮询以进行更斎ͼ另一U方法是服务器始l打开与浏览器的连接ƈ在数据可用时发送给览器。长期连接技术被UCؓ CometQ请参阅 参考资?/a>Q。本文将展示如何l合使用 Jetty servlet 引擎?DWR h效地实现一?Comet Web 应用E序?/p>
?
询方法的主要~点是:当扩展到更多客户机时Q将生成大量的通信量。每个客h必须定期讉K服务器以查更斎ͼqؓ服务器资源添加了更多负荷。最坏的一U情
冉|对不频繁发生更新的应用程序用轮询,例如一U?Ajax 邮g
Inbox。在q种情况下,相当数量的客h轮询是没有必要的Q服务器对这些轮询的回答只会?
“没有产生新数?#8221;。虽然可以通过增加轮询的时间间隔来减轻服务器负P但是q种Ҏ会生不良后果,卛_gq客hҎ务器事g的感知。当Ӟ很多应用E?
序可以实现某U权衡,从而获得可接受的轮询方法?/p>
管如此Q吸引h们?Comet
{略的其中一个优Ҏ其显而易见的高效性。客h不会像用轮询方法那L成烦人的通信量,q且事g发生后可立即发布l客h。但是保持长期连接处于打开
状态也会消耗服务器资源。当{待状态的 servlet 持有一个持久性请求时Q该 servlet 会独占一个线E。这限?Comet 对传l?
servlet 引擎的可伸羃性,因ؓ客户机的数量会很快超q服务器栈能有效处理的线E数量?/p>
![]() ![]() |
![]()
|
Jetty 6 的目的是扩展大量同步q接Q?Java™ 语言的非d I/OQ?code>java.nioQ库q用一个经q优化的输出~冲架构Q参?参考资?/a>Q。Jetty qؓ处理长期q接提供了一些技巧:该特性称?Continuations?
我将使用一个简单的 servlet ?Continuations q行演示Q这?servlet
接受请求,{待处理Q然后发送响应。接下来Q我展C当客户机数量超q服务器提供的处理线E后发生的状c最后,我将使用
Continuations 重新实现 servletQ您了?Continuations 在其中扮演的角色?/p>
Z便于理解下面的示例,我将?Jetty servlet 引擎限制在一个单h处理U程?a >清单 1ThreadPool
使用三个U程QJetty 服务器本w用一个线E,另一U程q行 HTTP q接器,侦听到来的请求。第三个U程执行 servlet 代码?/p>
清单 1. 单个 servlet U程?Jetty 配置
|
接下来,Z模拟对异步事件的{待Q?a >清单 2 展示?BlockingServlet
?service()
ҎQ该Ҏ?Thread.sleep()
调用在线E结束之前暂?2000 毫秒的时间。它q在执行开始和l束时输出系l时间。ؓ了区别输出和不同的请求,q将作ؓ标识W的h参数记录在日志中?/p>
清单 2. BlockingServlet
|
现在可以观察?servlet 响应一些同步请求的行ؓ?a >清单 3 展示了控制台输出Q五个?lynx
的ƈ行请求。命令行启动五个 lynx
q程Q将标识序号附加在请?URL 的后面?
|
清单 3 中的输出和预期一栗因?Jetty 只可以用一个线E执?servlet ?service()
Ҏ。Jetty 对请求进行排列,q按序提供服务。当针对某请求发出响应后立xC时间戳Q一?end
消息Q,servlet 接着处理下一个请求(后箋?start
消息Q。因此即使同时发Z个请求,其中一个请求必ȝ?8 U钟的时间才能接?servlet 处理?/p>
h意,?servlet 被阻塞时Q执行Q何操作都无济于事。这D代码模拟了h{待来自应用E序不同部分的异步事件。这里用的服务器既不是 CPU 密集型也不是 I/O 密集型:只有U程池耗尽之后才会对请求进行排队?
现在Q查?Jetty 6 ?Continuations Ҏ如何ؓq类情Ş提供帮助?a >清单 4 展示?清单 2 中?Continuations API 重写后的 BlockingServlet
。我稍后解释这些代码?/p>
清单 4. ContinuationServlet
|
清单 5 展示了对 ContinuationServlet
的五个同步请求的输出Q请?清单 3 q行比较?/p>
清单 5. ?ContinuationServlet 的五个ƈ发请求的输出
|
清单 5 中有两处需要重Ҏ意。首先,每个 start
消息出现两次Q先不要着急。其ơ,更重要的一点,h现在不需排队p够ƈ发处理,注意所?start
?end
消息的时间戳是相同的。因此,每个h的处理时间不会超q两U,即只运行一?servlet U程?/p>
![]() ![]() |
![]()
|
理解?Jetty Continuations 机制的实现原理,您就能够解释 清单 5 中的现象。要使用 ContinuationsQ必d Jetty q行配置Q以使用?SelectChannelConnector
处理h。这个连接器构徏?java.nio
API 之上Q因此它能够不用消耗每个连接的U程可以持有开攄q接。当使用 SelectChannelConnector
ӞContinuationSupport.getContinuation()
提供一?SelectChannelConnector.RetryContinuation
实例。(然而,您应该只针对 Continuation
接口q行~码Q请参阅 Portability and the Continuations API。)当对 RetryContinuation
调用 suspend()
Ӟ它将抛出一个特D的q行时异?—? RetryRequest
—?该异常将传播?servlet 以外q过qo器链传回Qƈ?SelectChannelConnector
捕获?但是发生该异怹后ƈ没有响应发送给客户机,h被放到处于等待状态的 Continuation
队列中,?HTTP q接仍然保持打开状态。此Ӟh提供服务的线E将q回 ThreadPool
Q用以ؓ其他h提供服务?
![]() |
|
暂停的请求将一直保持在{待状态的 Continuation
队列Q直到超出指定的旉Q或者当?resume()
Ҏ?Continuation
调用 resume()
ӞE后详l介l)。出CqCQ意一U条件时Q请求将被重新提交到 servletQ通过qo器链Q。事实上Q整个请求被重新q行处理Q直到首ơ调?suspend()
。当执行W二ơ发?suspend()
调用ӞRetryRequest
异常不会被抛出,执行照常q行?
现在应该可以解释 清单 5 中的输出了。每个请求依ơ进?servlet ?service()
Ҏ后,发?start
消息q行响应Q?code>Continuation ?suspend()
Ҏ引发 servlet 异常Q将释放U程使其处理下一个请求。所有五个请求快速通过 service()
Ҏ的第一部分Qƈq入{待状态,q且所?start
消息在几毫U内输出。两U后Q当过 suspend()
的时限后Q将从等待队列中索第一个请求,q将光新提交给 ContinuationServlet
。第二次输出 start
消息Q立卌回对 suspend()
的第二次调用Qƈ且发?end
消息q行响应。然后将在此执行 servlet 代码来处理队列中的下一个请求,以此cL?/p>
因此Q在 BlockingServlet
?ContinuationServlet
两种情况中,h被放入队列中以访问单?servlet U程。然而,虽然 servlet U程执行期间 BlockingServlet
发生两秒暂停Q?code>SelectChannelConnector 中的 ContinuationServlet
的暂停发生在 servlet 之外?code>ContinuationServlet 的d吐量更高一些,因ؓ servlet U程没有大部分旉用在 sleep()
调用中?/p>
![]() ![]() |
![]()
|
现在您已l了解到 Continuations 能够不消耗线E就可以暂停 servlet hQ我需要进一步解?Continuations API 以向您展C如何在实际应用中用?/p>
resume()
Ҏ生成一?suspend()
。可以将它们视ؓ标准?Object wait()
/notify()
机制?Continuations {h体。就是说Q?code>suspend() ?Continuation
Q因此也包括当前Ҏ的执行)处于暂停状态,直到出旉Q或者另一个线E调?resume()
?code>suspend()/resume()
对于实现真正使用 Continuations ?Comet 风格的服务非常关键。其基本模式是:从当前请求获?Continuation
Q调?suspend()
Q等待异步事件的到来。然后调?resume()
q生成一个响应?
然而,?Scheme q种语言中真正的语言U别?continuations 或者是 Java 语言?wait()
/notify()
范例不同的是Q对 Jetty Continuation
调用 resume()
q不意味着代码会从中断的地方l执行。正如您刚刚看到的,实际上和 Continuation
相关的请求被重新处理。这会生两个问题:重新执行 清单 4 中的 ContinuationServlet
代码Q以及丢q态:卌?suspend()
时丢׃用域内所有内宏V?/p>
W一个问题的解决Ҏ是?isPending()
Ҏ。如?isPending()
q回gؓ trueQ这意味着之前已经调用q一?suspend()
Q而重新执行请求时q没有发生第二次 suspend()
调用。换a之,Ҏ isPending()
条g在执?suspend()
调用之前q行代码Q这样将保Ҏ个请求只执行一ơ。在 suspend()
调用h{幂性之前,最好先对应用程序进行设计,q样即调用两次也不会出现问题,但是某些情况下无法?isPending()
Ҏ?code>Continuation 也提供了一U简单的机制来保持状态:putObject(Object)
?getObject()
Ҏ。在 Continuation
发生暂停Ӟ使用q两U方法可以保持上下文对象以及需要保存的状态。您q可以用这U机制作为在U程之间传递事件数据的方式Q稍后将演示q种Ҏ?/p>
![]() ![]() |
![]()
|
?
为实际示例场景,我将开发一个基本的 GPS 坐标跟踪 Web
应用E序。它在不规则的旉间隔内生成随机的l纬度值对。发挥一下想象力Q生成的坐标值可能就是͘q的一个公pR站、随w携带着 GPS
讑֤的马拉松选手、汽车拉力赛中的汽R或者运输中的包裏Vo人感兴趣的是我将如何告诉览器这个坐标?a >?1 展示了这个简单的 GPS 跟踪器应用程序的cdQ?/p>
?1. 昄 GPS 跟踪器应用程序主要组件的cd
首先Q应用程序需要某U方法来生成坐标。这由 RandomWalkGenerator
完成。从一对初始坐标对开始,每次调用它的U有 generateNextCoord()
ҎӞ从该位|移动随机指定的距离Qƈ新的位|作?GpsCoord
对象q回。初始化完成后,RandomWalkGenerator
生成一个线E,该线E以随机的时间间隔调?generateNextCoord()
Ҏq将生成的坐标发送给M注册?addListener()
?CoordListener
实例?a >清单 6 展示?RandomWalkGenerator
循环的逻辑Q?/p>
清单 6. RandomWalkGenerator's run() Ҏ
|
CoordListener
是一个回调接口,仅仅定义 onCoord(GpsCoord coord)
Ҏ。在本例中,ContinuationBasedTracker
cd?CoordListener
?code>ContinuationBasedTracker 的另一个公有方法是 getNextPosition(Continuation, int)
?a >清单 7 展示了这些方法的实现Q?/p>
清单 7. ContinuationBasedTracker l构
|
当客h使用 Continuation
调用 getNextPosition()
ӞisPending
Ҏ检查此时的h是否是第二次执行Q然后将它添加到{待坐标?Continuation
集合中。然后该 Continuation
被暂停。同ӞonCoord
—?生成新坐标时被调用 —?循环遍历所有处于等待状态的 Continuation
Q对它们讄 GPS 坐标Qƈ重新使用它们。之后,每个再次执行的请求完?getNextPosition()
执行Q从 Continuation
?GpsCoord
q将其返回给调用者。注意此处的同步需求,是ؓ了保?pendingContinuations
集合中的实例状态不会改变,q确保新增的 Continuation
在暂停之前没有被处理q?/p>
最后一个难Ҏ servlet 代码本nQ如 清单 8 所C:
|
如您所见,servlet 只执行了很少的工作。它仅仅获取了请求的 Continuation
Q调?getNextPosition()
Q将 GPSCoord
转换?JavaScript Object Notation (JSON)Q然后输出。这里不需要防止重新执行,因此我不必检?isPending()
?a >清单 9 展示了调?GpsTrackerServlet
的输出,同样Q有五个同步h而服务器只有一个可用线E:
|
q个CZq不引h注意Q但是提供了概念证明。发求后Q它们将一直保持打开的连接直至生成坐标,此时快速生成响应。这?Comet 模式的基本原理,Jetty 使用q种原理在一个线E内处理 5 个ƈ发请求,q都?Continuations 的功功?/p>
![]() ![]() |
![]()
|
现在您已l了解了如何使用 Continuations 在理Z创徏非阻?Web 服务Q您可能想知道如何创建客L代码来用这U功能。一?Comet 客户机需要完成以下功能:
XMLHttpRequest
q接Q直到收到响应?/li>
dojo.io.cometd
?
然而,如果服务器?Java 语言Q?DWR 2 可以同时在客h和服务器上获?Comet 高支持Q这是一U不错的ҎQ参?参考资?/a>Q。如果您q不了解 DWR 的话Q请参阅本系列第 3 部分 “l合 Direct Web Remoting 使用 Ajax”。DWR 透明地提供了一U?HTTP-RPC 传输层,您?Java 对象公开l网l中 JavaScript 代码的调用。DWR 生成客户端代理,自动封送和解除送数据,处理安全问题Q提供方便的客户端实用工具库Qƈ可以在所有主要浏览器上工作?
![]() ![]() |
![]()
|
DWR 2 最新引入了 Reverse Ajax 概念。这U机制可以将服务器端事g “推入” 到客h。客L DWR 代码透明地处理已建立的连接ƈ解析响应Q因此从开发h员的角度来看Q事件是从服务器?Java 代码L地发布到客户Z?/p>
DWR l过配置之后可以使用 Reverse Ajax 的三U不同机制。第一U就是较为熟悉的轮询Ҏ。第二种UCؓ piggybackQ? q种机制q不创徏M到服务器的连接,相反Q将一直等待直臛_生另一?DWR 服务Qpiggybacks 使事件等待该h的响应。这使它h较高的效率,但也意味着客户Z仉知被gq到直到发生另一个不相关的客h调用。最后一U机制用长期的? Comet 风格的连接。最妙的是,当运行在 Jetty 下时QDWR 能够自动ƈ切换Z?ContiuationsQ实现非d Comet?/p>
我将?GPS CZ中结合?Reverse Ajax ?DWR 2。通过q种演示Q您对 Reverse Ajax 的工作原理有更多的了解?/p>
此时不再需要?servlet。DWR 提供了一个控制器 servletQ它在 Java 对象之上直接转交客户求。同样也不需要显式地处理 ContinuationsQ因?DWR 在内部q行处理。因此我只需要一个新?CoordListener
实现Q将坐标更新发布到到M客户机浏览器上?/p>
ServerContext
接口提供?DWR ?Reverse Ajax 功能?code>ServerContext 可以察觉到当前查看给定页面的所?Web 客户机,q提供一?ScriptSession
q行怺通信?code>ScriptSession 用于?Java 代码?JavaScript 片段推入到客h?a >清单 10 展示?ReverseAjaxTracker
响应坐标通知的方式,q用它们生成对客户?updateCoordinate()
函数的调用。注意对 DWR ScriptBuffer
对象调用 appendData()
自动把 Java 对象送给 JSONQ如果用合适的转换器)?/p>
清单 10. ReverseAjaxTracker 中的通知回调Ҏ
|
接下来,必须?DWR q行配置以感?ReverseAjaxTracker
的存在。在大型应用E序中,可以使用 DWR ?Spring 集成提供 Spring 生成?bean。但是,在本例中Q我仅?DWR 创徏了一?ReverseAjaxTracker
新实例ƈ其攑ֈ application
范围中。所有后l请求将讉Kq个实例?/p>
我还需告诉 DWR 如何数据从 GpsCoord
beans 送到 JSON。由?GpsCoord
是一个简单对象,DWR 的基于反的 BeanConverter
可以完成此功能?a >清单 11 展示?ReverseAjaxTracker
的配|:
|
create
元素?javascript
属性指定了 DWR 用于跟t器公开?JavaScript 对象的名字,在本例中Q我的客L代码没有使用该属性,而是数据从跟踪器推入到其中。同?
Q还需?web.xml q行额外的配|,以针?Reverse Ajax 配置 DWRQ如 清单 12 所C:
|
W一?servlet init-param
Q?code>activeReverseAjaxEnabled 激z轮询和 Comet 功能。第二个 initApplicationScopeCreatorsAtStartup
通知 DWR 在应用程序启动时初始?ReverseAjaxTracker
。这在?bean 生成W一个请求时改写延迟初始化(lazy initializationQ的常规行ؓ —?在本例中q是必须的,因ؓ客户Z会主动对 ReverseAjaxTracker
调用Ҏ?/p>
最后,我需要实现调用自 DWR 的客L JavaScript 函数。将向回调函?—?updateCoordinate()
—?传?GpsCoord
Java bean ?JSON 表示Q由 DWR ?BeanConverter
自动序列化。该函数从坐标中提?longitude
?latitude
字段Qƈ通过调用 Document Object Model (DOM) 它们附加到列表中?a >清单 13 展示了这一q程Q以及页面的 onload
函数?code>onload 包含?dwr.engine.setActiveReverseAjax(true)
的调用,通知 DWR 打开与服务器的持久连接ƈ{待回调?/p>
清单 13. ?Reverse Ajax GPS 跟踪器的客户端实?/strong>
|
![]() |
|
现在我可以将览器指向跟t器面QDWR 在生成坐标数据时把数据推入客户机。该实现输出生成坐标的列表,??2 所C:
可以看到Q?Reverse Ajax 创徏事g驱动?Ajax 应用E序非常单。请CQ正是由?DWR 使用?Jetty ContinuationsQ当客户机等待新事g到来时不会占用服务器上面的线E?/p>
此时Q集成来?Yahoo! ?Google 的地N仉常简单。通过更改客户端回调,可轻村֜坐标传送到地图 APIQ而不是直接附加到面中?a >?3 展示?DWR Reverse Ajax GPS 跟踪器在此类地图lg上标l随\U:
![]() ![]() |
![]()
|
?
q本文,您了解了如何l合使用 Jetty Continuations ?Comet Z仉?Ajax
应用E序提供高效的可扩展解决Ҏ。我没有l出 Continuations
可扩展性的具体数字Q因为实际应用程序的性能取决于多U变化的因素。服务器g、所选择的操作系l、JVM 实现、Jetty
配置以及应用E序的设计和通信量配|文仉会媄?Jetty Continuations 的性能。然而,Webtide ?Greg
WilkinsQ主要的 Jetty 开发h员)曄发布了一份关?Jetty 6 的白皮书Q对使用 Continuations 和没有?
Continuations ?Comet 应用E序的性能q行了比较,该程序同时处?10000 个ƈ发请求(参阅 参考资?/a>Q。在 Greg 的测试中Q?Continuations 能够减少U程消耗,q同时减了过 10 倍的栈内存消耗?/p>
?
q看C使用 DWR ?Reverse Ajax 技术实C仉?Ajax 应用E序是多么简单。DWR
不仅省去了大量客L和服务器端编码,而且 Reverse Ajax q从代码中将完整的服务器-推送机制抽象出来。通过更改 DWR
的配|,您可以自由地?Comet、轮询,甚至?piggyback
Ҏ之间q行切换。您可以Ҏq行实验Qƈ扑ֈ适合自己应用E序的最x能{略Q同时不会媄响到自己的代码?/p>
如果希望对自q Reverse Ajax 应用E序q行实验Q下载ƈ研究 DWR 演示E序的代码(DWR 源代码发行版的一部分Q参?参考资?/a>Q将非常有帮助。如果希望亲自运行示例,q可获得本文使用的示例代码(参见 下蝲Q?/p>
![]() ![]() |
![]()
|
描述 | 名字 | 大小 | 下蝲Ҏ |
---|---|---|---|
CZ代码 | jetty-dwr-comet-src.tgz | 8KB | HTTP |
![]() |
||||
![]() |
关于下蝲Ҏ的信?/a> | ![]() |