DWR 入门与应用(一)
Java 开发人员与网页设计人员的桥梁 DWR…呃!我懒得写简介了…直接来看看可以做什么吧!…
请先到 http://getahead.ltd.uk/dwr/ 下载 dwr.jar,放到WEB-INF/lib下…
负责处理客户端请求,并呼叫Java对象的是DWRServlet,DWR其实也有些Model 2的味道,只是View的这一层比较弱,因为放到客户端的JavaScript应用程序中…
在web.xml中加入DWRServlet…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?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吧!
1 2 3 4 5 6 7 | package onlyfun.caterpillar; public class Hello { public String hello(String name) { return "哈啰!" + name + "!您的第一个DWR!"; } } |
客户端要呼叫这个Java对象,传给它参数,而后传回一个字符串,客户端再显示这个字符串,神奇?其实是要告诉DWRServlet这件事,这需要一个dwr.xml:
1 2 3 4 5 6 7 8 9 10 11 | <?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> |
creator设定为new,表示使用Hello的无参数建构子来生成对象,javascript设定为Hello,表示客户端JavaScript程序可以使用Hello来呼叫对应的onlyfun.caterpillar.Hello物件。
来写个客户端的网页,当中有一个输入字段…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>第一个DWR程序</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='哈啰' οnclick='hello();' /> <div id="result"></div> </body> </html> |
dwr/interface/Hello.js是由DWRServlet根据dwr.xml中的设定生成的,engine.js负责客户端伺服端沟通,util.js是一些好用的JavaScript程序,可以让您少写很多JavaScript。
hello.js是我们自订的函式,按下按钮后,会呼叫当中的hello()函式:
1 2 3 4 5 6 7 8 | function hello() { var user = $('user').value; Hello.hello(user, callback); } function callback(msg) { DWRUtil.setValue('result', msg); } |
${'user'}取得输入字段的DOM对象,value取得当中的字段值,而后呼叫Hello.hello(),并将value当作参数传送… 结果是呼叫Server端的Hello Java对象,当结果传回后,会呼叫JavaScript的callback函式,DWRUtil的setValue()方法会将传回的msg设定给指定 id的DOM,结果就是…啥!AJAX的功能在哪…就这个而言就是发出异步请求,而响应不用Refresh页面啦!
好啦!这个无聊的Hello DWR可以做啥!…XD
已经可以让您做个简单的文字提示功能了…像这个…
http://caterpillar.onlyfun.net/Gossip/index.html
把鼠标指到书的照片上,会显示提示文字,这些提示文字本身不是存在网页上的,而是在Server端,当鼠标指到书上时,会用Request object去抓,然后显示在框框中…
当然!我的网站只支持PHP,所以那不是DWR完成的功能,而且我是直接用Request object跟DOM去慢慢刻的…对初学者来说已经有些麻烦了…XD
不过!用DWR就可以很简单完成这个功能…
先写个Java类别吧!会抓properties档案中的文字讯息,例如…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } } |
从程序中就知道,它会去抓book_zh_TW.properties的数据,这不是重点啦!只是Java的一个功能,我们要看的是DWR,不过先把book_zh_TW.properties准备好…
1 2 3 | java=Java 学习笔记的介绍 … BlaBla... spring=Spring 技术手册的介绍…BlaBla... ajax=Ajax in action 中文版的介绍… |
唔!里头是中文字,自己用native2ascii转换吧…这也不是重点…我们是要看DWR怎么做到文字提示功能…
一样的…要开放这个Book对象,在dwr.xml中…
1 2 3 4 5 6 7 8 9 10 11 | <?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> |
scope设定为application,表示这个Book对象在整个应用程序阶段都活着。
然后,客户端写个网页…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <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>个人着/译作</title> </head> <body> <div id="ajax" οnmοuseοver="getBookData(this);" οnmοuseοut="clearData();"><a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&bn=AXP011800"><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" οnmοuseοver="getBookData(this);" οnmοuseοut="clearData();"><a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&bn=ACL021000"><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" οnmοuseοver="getBookData(this);" οnmοuseοut="clearData();"><a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&bn=ACL020931"><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跟onmouseout,鼠标移入与移出时会呼叫的函式,还有最下面的info,抓回来的书籍介绍会放到当中…
book.js如下,简单的很…
1 2 3 4 5 6 7 8 9 10 11 | function getBookData(ele) { Book.getDescription(ele.id, setBookData); } function setBookData(description) { DWRUtil.setValue('info', description); } function clearData() { DWRUtil.setValue('info', ''); } |
程序很简单,我懒得解释了…XD
看一下画面好了…这是鼠标移到 Ajax in action中文版 上的介绍画面…
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1186566
假设您要从数据库中查询出一些字符串,然后填写到窗体的下拉选单中。
例如一个示意的Java程序如下:
1 2 3 4 5 6 7 8 | package onlyfun.caterpillar; public class Option { public String[] getOptions() { // 实际上这些字符串是从数据库中查到的啦… return new String[] {"良葛格", "毛美眉", "米小狗"}; } } |
传回的字符串数组,您要填写到下拉选单中,当然,首先我们要在dwr.xml中开发这个对象…
1 2 3 4 5 6 7 8 9 10 | <?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> |
这是我们的网页…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <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> 选项: <select id="opts"></select> </body> </html> |
传回的字符串数组会填入opts这个select中,我们的option.js如下…
1 2 3 4 5 6 7 8 | window.onload = function() { OPT.getOptions(populate); }; function populate(list){ DWRUtil.removeAllOptions("opts"); DWRUtil.addOptions("opts", list); } |
够简单了…不需要解释了…
看一下结果…
好啦!我知道有人在说了,这个程序有够无聊…
改一下!就是个不错的范例了,例如连动方块,唔!在Ajax in action中叫啥?Dynamic double combo?…
假设一个会去从数据库中查询数据的Java程序示意如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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()分别表示产品的年份跟型号,这边用Map仿真,实际上数据是来自数据库的查询。
一样的,在dwr.xml中设定:
1 2 3 4 5 6 7 8 9 10 | <?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> |
我们会有个脚踏车年份与型号查询页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <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 οnlοad="refreshYearList();"> 年份:<select id="years" οnchange="refreshBikeList();"></select><br/><br/> 型号:<select id="bikes"></select><br/> </body> </html> |
注意,在选完第一个年份后,会触发onchange事件,接着第二个下拉选单会自动填上对应年份的型号,而不是按钮按下,再去取得第二个下拉选单,然后refresh...blah...blah...
bike.js如下…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package onlyfun.caterpillar; import java.io.IOException; import java.io.PrintWriter; import java.util.LinkedList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ChatRoomServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet { private static LinkedList<Message> messages = new LinkedList<Message>(); public ChatRoomServlet() { super(); } private List<Message> addMessage(String text) { if (text != null && text.trim().length() > 0) { messages.addFirst(new Message(text)); while (messages.size() > 10) { messages.removeLast(); } } return messages; } private List<Message> getMessages() { return messages; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Message> list = null; if("send".equals(request.getParameter("task"))) { list = addMessage(request.getParameter("msg")); } else if("query".equals(request.getParameter("task"))){ list = getMessages(); } PrintWriter out = response.getWriter(); response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); out.println("<response>"); for(int i = 0; i < list.size(); i++) { String msg = list.get(i).getText(); out.println("<message>" + msg + "</message>"); } out.println("</response>"); } } |
Message物件如下…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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接受讯息新增与查询,判断的方式是检查请求参数task是send或query。
Servlet会以XML传回目前List当中的讯息,客户端可以查询或插入新讯息时,取得目前List中的讯息,接着在web.xml中设定一下…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?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> |
在网页中,使用者可以在输入讯息后按下按钮送出信息,并在XML响应取得时,将讯息以一列一列的表格方式显示出来,另外还设定了周期性的轮询,即使不输入新讯息,也可以周期性的取得新的聊天讯息…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <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" οnclick="sendMessage()"/> </p> <p>Messages:</p> <table align="left"> <tbody id="dynamicUpdateArea"></tbody> </table> </body> </html> |
简单抓个画面…
直接用AJAX,后端用JSP/Servlet,您要对请求参数做些判断,看看是新增讯息或查询,并要自行输出XML,有的没的…
改成DWR的话,就很简单了,写个简单的Java对象…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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中开放一下…
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?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> |
使用者取得讯息时,是直接传回List对象,而List中装的是Message对象,Message对象是自订对象,conterver设定为 bean,表示DWR会自动将Message的getter名称转换为传回客户端的JavaScript对象中的属性,例如Message中有 getText(),则在客户端可以用message.text这样的方式来存取。
接着是简单的客户端网页…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <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" οnclick="sendMessage()"/> </p> <p>Messages:</p> <div id="chatlog"></div> </body> </html> |
当List对象传回时,它成为gotMessages(messages)中的messages对象,而messages对象中包括 Message对象转换后对应的JavaScript对象,由于我们已经设定了Converter,所以可以用messages[data].text来 取得传回的讯息…
简单抓个画面…