您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何利用Spring整合DWR comet 實(shí)現(xiàn)無刷新的多人聊天室”,在日常操作中,相信很多人在如何利用Spring整合DWR comet 實(shí)現(xiàn)無刷新的多人聊天室問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何利用Spring整合DWR comet 實(shí)現(xiàn)無刷新的多人聊天室”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
用dwr的comet(推)來實(shí)現(xiàn)簡單的無刷新多人聊天室,comet是長連接的一種。通常我們要實(shí)現(xiàn)無刷新,一般會使用到Ajax。Ajax 應(yīng)用程序可以使用兩種基本的方法解決這一問題:一種方法是瀏覽器每隔若干秒時(shí)間向服務(wù)器發(fā)出輪詢以進(jìn)行更新,另一種方法是服務(wù)器始終打開與瀏覽器的連接并在數(shù)據(jù)可用時(shí)發(fā)送給瀏覽器。***種方法一般利用setTimeout或是setInterval定時(shí)請求,并返回***數(shù)據(jù),這無疑增加了服務(wù)器的負(fù)擔(dān),浪費(fèi)了大量的資源。而第二種方法也會浪費(fèi)服務(wù)器資源,長期的建立連接;而相對***種來說,第二種方式會更優(yōu)于***種方法;這里有一個(gè)一對多和多對一的關(guān)系,而comet向多個(gè)客戶端推送數(shù)據(jù)就是一對多的關(guān)系。而具體使用哪種方式,要看你當(dāng)前的需求而定,沒有絕對的。
為什么使用Comet?
輪詢方法的主要缺點(diǎn)是:當(dāng)擴(kuò)展到更多客戶機(jī)時(shí),將生成大量的通信量。每個(gè)客戶機(jī)必須定期訪問服務(wù)器以檢查更新,這為服務(wù)器資源添加了更多負(fù)荷。最壞的一種情況是對不頻繁發(fā)生更新的應(yīng)用程序使用輪詢,例如一種 Ajax 郵件 Inbox。在這種情況下,相當(dāng)數(shù)量的客戶機(jī)輪詢是沒有必要的,服務(wù)器對這些輪詢的回答只會是 “沒有產(chǎn)生新數(shù)據(jù)”。雖然可以通過增加輪詢的時(shí)間間隔來減輕服務(wù)器負(fù)荷,但是這種方法會產(chǎn)生不良后果,即延遲客戶機(jī)對服務(wù)器事件的感知。當(dāng)然,很多應(yīng)用程序可以實(shí)現(xiàn)某種權(quán)衡,從而獲得可接受的輪詢方法。
盡管如此,吸引人們使用 Comet 策略的其中一個(gè)優(yōu)點(diǎn)是其顯而易見的高效性。客戶機(jī)不會像使用輪詢方法那樣生成煩人的通信量,并且事件發(fā)生后可立即發(fā)布給客戶機(jī)。但是保持長期連接處于打開狀態(tài)也會消耗服務(wù)器資源。當(dāng)?shù)却隣顟B(tài)的 servlet 持有一個(gè)持久性請求時(shí),該 servlet 會獨(dú)占一個(gè)線程。這將限制 Comet 對傳統(tǒng) servlet 引擎的可伸縮性,因?yàn)榭蛻魴C(jī)的數(shù)量會很快超過服務(wù)器棧能有效處理的線程數(shù)量。
如果本示例結(jié)合Jetty應(yīng)用服務(wù)器效果會更好。
開發(fā)環(huán)境:
System:Windows
WebBrowser:IE6+、Firefox3+
JavaEE Server:tomcat5.0.2.8、tomcat6
IDE:eclipse、MyEclipse 8
開發(fā)依賴庫:
JavaEE5、Spring 3.0.5、dwr 3
Email:hoojo_@126.com
Blog:http://blog.csdn.net/IBM_hoojo or http://hoojo.cnblogs.com/
一、準(zhǔn)備工作
1、 下載dwr的相關(guān)jar包
https://java.net/downloads/dwr/Development%20Builds/Build%20116/dwr.jar
程序中還需要spring的相關(guān)jar包
http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE
需要的jar包如下
2、 建立一個(gè)WebProject,名稱DWRComet
在web.xml中添加dwr、spring配置如下:
<-- 加載Spring容器配置 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <-- 設(shè)置Spring容器加載配置文件路徑 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.directwebremoting.servlet.DwrListener</listener-class> </listener> <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> <-- dwr的comet控制 --> <init-param> <param-name>pollAndCometEnabled</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>
3、 在src目錄加入applicationContext-beans.xml配置,這個(gè)配置專門配置bean對象,用來配置需要注入的對象。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> </beans>
4、 在WEB-INF目錄添加dwr.xml文件,基本代碼如下
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> </dwr>
以上的準(zhǔn)備基本完畢,下面來完成無刷新聊天室代碼
1、 聊天實(shí)體類Model
package com.hoo.entity; import java.util.Date; /** * <b>function:</b> * @author hoojo * @createDate 2011-6-3 下午06:40:07 * @file Message.java * @package com.hoo.entity * @project DWRComet * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class Message { private int id; private String msg; private Date time; //getter、setter }
2、 編寫聊天信息的事件
package com.hoo.chat; import org.springframework.context.ApplicationEvent; /** * <b>function:</b>發(fā)送聊天信息事件 * @author hoojo * @createDate 2011-6-7 上午11:24:21 * @file MessageEvent.java * @package com.hoo.util * @project DWRComet * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class ChatMessageEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; public ChatMessageEvent(Object source) { super(source); } }
繼承ApplicationEvent,構(gòu)造參數(shù)用于傳遞發(fā)送過來的消息。這個(gè)事件需要一個(gè)監(jiān)聽器監(jiān)聽,一旦觸發(fā)了這個(gè)事件,我們就可以向客戶端發(fā)送消息。
3、 發(fā)送消息服務(wù)類,用戶客戶端發(fā)送消息。dwr需要暴露這個(gè)類里面的發(fā)送消息的方法
package com.hoo.chat; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.hoo.entity.Message; /** * <b>function:</b>客戶端發(fā)消息服務(wù)類業(yè)務(wù) * @author hoojo * @createDate 2011-6-7 下午02:12:47 * @file ChatService.java * @package com.hoo.chat * @project DWRComet * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class ChatService implements ApplicationContextAware { private ApplicationContext ctx; public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.ctx = ctx; } /** * <b>function:</b> 向服務(wù)器發(fā)送信息,服務(wù)器端監(jiān)聽ChatMessageEvent事件,當(dāng)有事件觸發(fā)就向所有客戶端發(fā)送信息 * @author hoojo * @createDate 2011-6-8 下午12:37:24 * @param msg */ public void sendMessage(Message msg) { //發(fā)布事件 ctx.publishEvent(new ChatMessageEvent(msg)); } }
上面的sendMessage需要瀏覽器客戶端調(diào)用此方法完成消息的發(fā)布,傳遞一個(gè)Message對象,并且是觸發(fā)ChatMessageEvent事件。
4、 編寫監(jiān)聽器監(jiān)聽客戶端是否觸發(fā)ChatMessageEvent
package com.hoo.chat; import java.util.Collection; import java.util.Date; import javax.servlet.ServletContext; import org.directwebremoting.ScriptBuffer; import org.directwebremoting.ScriptSession; import org.directwebremoting.ServerContext; import org.directwebremoting.ServerContextFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.web.context.ServletContextAware; import com.hoo.entity.Message; /** * <b>function:</b>監(jiān)聽客戶端事件,想客戶端推出消息 * @author hoojo * @createDate 2011-6-7 上午11:33:08 * @file SendMessageClient.java * @package com.hoo.util * @project DWRComet * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @SuppressWarnings("unchecked") public class ChatMessageClient implements ApplicationListener, ServletContextAware { private ServletContext ctx; public void setServletContext(ServletContext ctx) { this.ctx = ctx; } @SuppressWarnings("deprecation") public void onApplicationEvent(ApplicationEvent event) { //如果事件類型是ChatMessageEvent就執(zhí)行下面操作 if (event instanceof ChatMessageEvent) { Message msg = (Message) event.getSource(); ServerContext context = ServerContextFactory.get(); //獲得客戶端所有chat頁面script session連接數(shù) Collection<ScriptSession> sessions = context.getScriptSessionsByPage(ctx.getContextPath() + "/chat.jsp"); for (ScriptSession session : sessions) { ScriptBuffer sb = new ScriptBuffer(); Date time = msg.getTime(); String s = time.getYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate() + " " + time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds(); //執(zhí)行setMessage方法 sb.appendScript("showMessage({msg: '") .appendScript(msg.getMsg()) .appendScript("', time: '") .appendScript(s) .appendScript("'})"); System.out.println(sb.toString()); //執(zhí)行客戶端script session方法,相當(dāng)于瀏覽器執(zhí)行JavaScript代碼 //上面就會執(zhí)行客戶端瀏覽器中的showMessage方法,并且傳遞一個(gè)對象過去 session.addScript(sb); } } } }
上面的代碼主要是監(jiān)聽客戶端的事件,一旦客戶端有觸發(fā)ApplicationEvent事件或是其子類,就會執(zhí)行onApplicationEvent方法。代碼中通過instanceof判斷對象實(shí)例,然后再執(zhí)行。如果有觸發(fā)ChatMessageEvent事件,就獲取所有連接chat.jsp這個(gè)頁面的ScriptSession。然后像所有的ScriptSession中添加script。這樣被添加的ScriptSession就會在有連接chat.jsp的頁面中執(zhí)行。
所以這就是客戶端為什么會執(zhí)行服務(wù)器端的JavaScript代碼。但前提是需要在web.xml中添加dwrComet配置以及在chat頁面添加ajax反轉(zhuǎn)。
5、 下面開始在bean容器和dwr的配置中添加我們的配置
applicationContext-beans.xml配置
<bean id="chatService" class="com.hoo.chat.ChatService"/> <bean id="chatMessageClient" class="com.hoo.chat.ChatMessageClient"/>
上面的chatService會在dwr配置中用到
dwr.xml配置
<allow> <convert match="com.hoo.entity.Message" converter="bean"> <param name="include" value="msg,time" /> </convert> <create creator="spring" javascript="ChatService"> <param name="beanName" value="chatService" /> </create> </allow>
charService的sendMessage方法傳遞的是Message對象,所以要配置Message對象的convert配置。
上面的create的creator是spring,表示在spring容器中拿chatService對象。里面的參數(shù)的beanName表示在spring容器中找name等于charService的bean對象。
6、 客戶端chat.jsp頁面代碼
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>Chat</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <script type="text/javascript" src="${pageContext.request.contextPath }/dwr/engine.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/dwr/util.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/dwr/interface/ChatService.js"></script> <script type="text/javascript"> function send() { var time = new Date(); var content = dwr.util.getValue("content"); var name = dwr.util.getValue("userName"); var info = encodeURI(encodeURI(name + " say:\n" + content)); var msg = {"msg": info, "time": time}; dwr.util.setValue("content", ""); if (!!content) { ChatService.sendMessage(msg); } else { alert("發(fā)送的內(nèi)容不能為空!"); } } function showMessage(data) { var message = decodeURI(decodeURI(data.msg)); var text = dwr.util.getValue("info"); if (!!text) { dwr.util.setValue("info", text + "\n" + data.time + " " + message); } else { dwr.util.setValue("info", data.time + " " + message); } } </script> </head> <body onload="dwr.engine.setActiveReverseAjax(true);"> <textarea rows="20" cols="60" id="info" readonly="readonly"></textarea> <hr/> 昵稱:<input type="text" id="userName"/><br/> 消息:<textarea rows="5" cols="30" id="content"></textarea> <input type="button" value=" Send " onclick="send()" style="height: 85px; width: 85px;"/> </body> </html>
首先,你需要導(dǎo)入dwr的engine.js文件,這個(gè)很重要,是dwr的引擎文件。其次你使用的那個(gè)類的方法,也需要在導(dǎo)入進(jìn)來。一般是interface下的,并且在dwr.xml中配置過的create。
上面的js中調(diào)用的charService類中的sendMessage方法,所以在jsp頁面中導(dǎo)入的是ChatService.js。
在body的onload事件中,需要設(shè)置反轉(zhuǎn)Ajax,這個(gè)很重要。
showMessage是ChatMessageClient的onApplicationEvent方法中的appendScript中需要執(zhí)行的方法。data參數(shù)也是在那里傳遞過來的。
每當(dāng)發(fā)送sendMessage方法后就會觸發(fā)ChatMessageEvent事件,然后監(jiān)聽的地方就會執(zhí)行onApplicationEvent方法,在這個(gè)方法中又會執(zhí)行瀏覽器中的showMessage方法。
到此,關(guān)于“如何利用Spring整合DWR comet 實(shí)現(xiàn)無刷新的多人聊天室”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。