Spring整合DWR comet 实现无刷新 多人聊天室
用dwr的comet(推)来实现简单的无刷新多人聊天室,comet是长连接的一种。通常我们要实现无刷新,一般会使用到Ajax。Ajax 应用程序可以使用两种基本的方法解决这一问题:一种方法是浏览器每隔若干秒时间向服务器发出轮询以进行更新,另一种方法是服务器始终打开与浏览器的连接并在数据可用时发送给浏览器。第一种方法一般利用setTimeout或是setInterval定时请求,并返回最新数据,这无疑增加了服务器的负担,浪费了大量的资源。而第二种方法也会浪费服务器资源,长期的建立连接;而相对第一种来说,第二种方式会更优于第一种方法;这里有一个一对多和多对一的关系,而comet向多个客户端推送数据就是一对多的关系。而具体使用哪种方式,要看你当前的需求而定,没有绝对的。
为什么使用 Comet?
轮询方法的主要缺点是:当扩展到更多客户机时,将生成大量的通信量。每个客户机必须定期访问服务器以检查更新,这为服务器资源添加了更多负荷。最坏的一种情况是对不频繁发生更新的应用程序使用轮询,例如一种 Ajax 邮件 Inbox。在这种情况下,相当数量的客户机轮询是没有必要的,服务器对这些轮询的回答只会是 “没有产生新数据”。虽然可以通过增加轮询的时间间隔来减轻服务器负荷,但是这种方法会产生不良后果,即延迟客户机对服务器事件的感知。当然,很多应用程序可以实现某种权衡,从而获得可接受的轮询方法。
尽管如此,吸引人们使用 Comet 策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量,并且事件发生后可立即发布给客户机。但是保持长期连接处于打开状态也会消耗服务器资源。当等待状态的 servlet 持有一个持久性请求时,该 servlet 会独占一个线程。这将限制 Comet 对传统 servlet 引擎的可伸缩性,因为客户机的数量会很快超过服务器栈能有效处理的线程数量。
如果本示例结合Jetty应用服务器效果会更好。
一、准备工作
1、 下载dwr的相关jar包
https://java.net/downloads/dwr/Development%20Builds/Build%20116/dwr.jar
程序中还需要spring的相关jar包
需要的jar包如下
2、 建立一个WebProject,名称DWRComet
在web.xml中添加dwr、spring配置如下:
<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5"> <!-- 加载Spring容器配置 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 设置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> <welcome-file-list>
<welcome-file>chat.jsp</welcome-file>
</welcome-file-list>
</web-app></span>
3、 在src目录加入applicationContext-beans.xml配置,这个配置专门配置bean对象,用来配置需要注入的对象。
<span style="font-size:18px;"><?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">
<bean id="chatService" class="com.tgb.chat.ChatService" /> <bean id="chatMessageClient" class="com.tgb.chat.ChatMessageClient" /> </beans></span>
4、 在WEB-INF目录添加dwr.xml文件,基本代码如下
<span style="font-size:18px;"><?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></span>
以上的准备基本完毕,下面来完成无刷新聊天室代码
二、聊天室相关业务实现
1、 聊天实体类Model
<span style="font-size:18px;">package com.tgb.entity; import java.util.Date; public class Message { private int id;
private String msg;
private Date time;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
} }
</span>
2、 编写聊天信息的事件
<span style="font-size:18px;">package com.tgb.chat; import org.springframework.context.ApplicationEvent; public class ChatMessageEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L; public ChatMessageEvent(Object source) { super(source); } }
</span>
继承ApplicationEvent,构造参数用于传递发送过来的消息。这个事件需要一个监听器监听,一旦触发了这个事件,我们就可以向客户端发送消息。
3、 发送消息服务类,用户客户端发送消息。dwr需要暴露这个类里面的发送消息的方法
<span style="font-size:18px;">package com.tgb.chat; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher; import com.tgb.entity.Message; public class ChatService implements ApplicationContextAware {
private ApplicationContext ctx; public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.ctx = ctx; } /** * <b>function:</b> 向服务器发送信息,服务器端监听ChatMessageEvent事件,当有事件触发就向所有客户端发送信息 * @author hoojo * @createDate 2011-6-8 下午12:37:24 * @param msg */ public void sendMessage(Message msg) { //发布事件 ctx.publishEvent(new ChatMessageEvent(msg)); } public void Init(){ }
}
</span>
上面的sendMessage需要浏览器客户端调用此方法完成消息的发布,传递一个Message对象,并且是触发ChatMessageEvent事件。
4、 编写监听器监听客户端是否触发ChatMessageEvent
<span style="font-size:18px;">package com.tgb.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.tgb.entity.Message; @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就执行下面操作 if (event instanceof ChatMessageEvent) { Message msg = (Message) event.getSource(); ServerContext context = ServerContextFactory.get(); //获得客户端所有chat页面script session连接数 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(); //执行setMessage方法 sb.appendScript("showMessage({msg: '") .appendScript(msg.getMsg()) .appendScript("', time: '") .appendScript(s) .appendScript("'})"); System.out.println(sb.toString()); //执行客户端script session方法,相当于浏览器执行JavaScript代码 //上面就会执行客户端浏览器中的showMessage方法,并且传递一个对象过去 session.addScript(sb); } } } }
</span>
上面的代码主要是监听客户端的事件,一旦客户端有触发ApplicationEvent事件或是其子类,就会执行onApplicationEvent方法。代码中通过instanceof判断对象实例,然后再执行。如果有触发ChatMessageEvent事件,就获取所有连接chat.jsp这个页面的ScriptSession。然后像所有的ScriptSession中添加script。这样被添加的ScriptSession就会在有连接chat.jsp的页面中执行。
所以这就是客户端为什么会执行服务器端的JavaScript代码。但前提是需要在web.xml中添加dwrComet配置以及在chat页面添加ajax反转。
5、 下面开始在bean容器和dwr的配置中添加我们的配置
applicationContext-beans.xml配置
<span style="font-size:18px;"><bean id="chatService" class="com.hoo.chat.ChatService"/>
<bean id="chatMessageClient" class="com.hoo.chat.ChatMessageClient"/></span>
上面的chatService会在dwr配置中用到
dwr.xml配置
<span style="font-size:18px;"><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></span>
charService的sendMessage方法传递的是Message对象,所以要配置Message对象的convert配置。
上面的create的creator是spring,表示在spring容器中拿chatService对象。里面的参数的beanName表示在spring容器中找name等于charService的bean对象。
6、 客户端chat.jsp页面代码
<span style="font-size:18px;"><%@ 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>微信内部聊天</title> <meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page"> <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 + " 说:\n" + content));
var msg = {
"msg" : info,
"time" : time
};
dwr.util.setValue("content", "");
if (!!content) { ChatService.sendMessage(msg); } else {
alert("发送的内容不能为空!");
}
}
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>
<script type="text/javascript">
window.onload = Init()
function Init() {
ChatService.Init();
alert("页面初始化加载成功啦");
}
</script>
</head> <body onload="dwr.engine.setActiveReverseAjax(true);">
<div id="info"
style="margin-top: 3px; padding-top: 3px; height: 89%; overflow-y: auto; border: 1px solid #000000;"></div>
<!-- <textarea rows="20" cols="60" id="" readonly="readonly"></textarea> -->
<hr /> <input type="text" id="userName" /> <div style="margin-bottom: 10px; height: 10%">
<input id="content" type="text" style="height: 45px; width: 74%"><input
type="button" value="发送" style="height: 48px; width: 25%"
onclick="send();">
</div> <!-- 原本的发送和输入消息框 -->
<!-- <textarea rows="5" cols="30" id="content"></textarea> -->
<!-- <input type="button" value=" Send " onclick="send()" -->
<!-- style="height: 85px; width: 85px;" /> -->
</body>
</html>
</span>
首先,你需要导入dwr的engine.js文件,这个很重要,是dwr的引擎文件。其次你使用的那个类的方法,也需要在导入进来。一般是interface下的,并且在dwr.xml中配置过的create。
上面的js中调用的charService类中的sendMessage方法,所以在jsp页面中导入的是ChatService.js。
在body的onload事件中,需要设置反转Ajax,这个很重要。
showMessage是ChatMessageClient的onApplicationEvent方法中的appendScript中需要执行的方法。data参数也是在那里传递过来的。
每当发送sendMessage方法后就会触发ChatMessageEvent事件,然后监听的地方就会执行onApplicationEvent方法,在这个方法中又会执行浏览器中的showMessage方法。
Spring整合DWR comet 实现无刷新 多人聊天室的更多相关文章
- 基于JQuery+JSP的无数据库无刷新多人在线聊天室
JQuery是一款非常强大的javascript插件,本文就针对Ajax前台和JSP后台来实现一个无刷新的多人在线聊天室,该实现的数据全部存储在服务端内存里,没有用到数据库,本文会提供所有源程序,需要 ...
- spring mvc + xmlHttpRequest2.0 实现无刷新上传文件,带进度条和剩余时间
1.springmvc支持文件上传,需要在spring-mvc.xml配置文件中加上下面的一段话: <!-- 支持上传文件 --> <bean id="multipartR ...
- 整合pjax无刷新
一:整合pjax的准备工作: 检查你的网站是否引入1.7.0版本以上的jquery.js,如果没有请全局引入 1.新浪CDN提速:<script type="text/javascri ...
- 二 SSH整合:Spring整合Hibernate,无障碍整合&无核心配置整合,Hibernate模版常用方法,
重建SSH项目 java项目可以直接复制,但是web项目除了改名字还要该配置,如下: 方式一:无障碍整合:带Hibernate配置文件 <?xml version="1.0" ...
- jQuery AJAX 网页无刷新上传示例
新年礼,提供简单.易套用的 jQuery AJAX 上传示例及代码下载.后台对文件的上传及检查,以 C#/.NET Handler 处理 (可视需要改写成 Java 或 PHP). 有时做一个网站项目 ...
- Mybatis与Spring整合,使用了maven管理项目,作为初学者觉得不错,转载下来
转载自:http://www.cnblogs.com/xdp-gacl/p/4271627.html 一.搭建开发环境 1.1.使用Maven创建Web项目 执行如下命令: mvn archetype ...
- Hibernate注解使用以及Spring整合
Hibernate注解使用以及Spring整合 原文转自:http://wanqiufeng.blog.51cto.com/409430/484739 (1) 简介: 在过去几年里,Hibernate ...
- Spring整合jdbc
首先web.xml文件跟往常一样,加载spring容器和加载org.springframework.web.context.ContextLoaderListener读取applicationCont ...
- Struts2+Hibernate+Spring 整合示例[转]
原文 http://blog.csdn.net/tkd03072010/article/details/7468769 Spring整合Struts2.Hibernate原理概述: 从用户角度来看,用 ...
随机推荐
- Struts1开山篇
·本次学习的是Struts1的最终版本--struts-1.3.10. ·开发环境: Java版本:1.8.0_131 Tomcat版本:apache-tomcat-9.0.0.M21 下下来完整安装 ...
- [SHOI2011]双倍回文
Description Input 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. Output 输出文件只有一行,即:输入数据中字符串的最长 ...
- POJ2449 Remmarguts' Date
"Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. ...
- [HNOI2005]狡猾的商人
题目描述 输入输出格式 输入格式: 从文件input.txt中读入数据,文件第一行为一个正整数w,其中w < 100,表示有w组数据,即w个账本,需要你判断.每组数据的第一行为两个正整数n和m, ...
- gift 分数规划的最大权闭合子图
题目大意: N个物品,物品间有M组关系,每个物品有一个ai的代价,满足关系后会得到bi的值 求 max(sigma(bi)/sigma(ai)) 题解: 很明显的最大权闭合子图,只不过需要处理分数. ...
- ●BZOJ 4710 [Jsoi2011]分特产
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4710 题解: 容斥,组合先看看这个方案数的计算:把 M 个相同的东西分给 N 个人,每个人可 ...
- SAM维护的在线LCS
题目大意: 给定两个字符串,存在三种操作,分别是在a,b串末尾加一个字符串,和询问两串的LCS 题解: Get新套路:把两串建在同一SAM上,将重合的位置合并为同一节点,再加个标记数组,如果两者的LC ...
- [HNOI2006]超级英雄
题目描述 现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或奖金.主持人问题准备了若干道题目,只有当选手正确回答一道题后,才 ...
- hdu 5538(水)
Input The first line contains an integer T indicating the total number of test cases. First line of ...
- hdu 5505(GT and numbers)
题意: 给你a和b,a每次和它的因子相乘得到一个新的a,求多少次后可以得到b. 输入样例 3 1 1 1 2 2 4 输出样例 0 -1 1 思路: 每次找出a和b/a的最大公约数(即当前a想得到b能 ...