Spring整合DWR comet 实现无刷新 多人聊天室

2024-06-03 23:18

本文主要是介绍Spring整合DWR comet 实现无刷新 多人聊天室,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring整合DWR comet 实现无刷新 多人聊天室

dwrcomet(推)来实现简单的无刷新多人聊天室,comet是长连接的一种。通常我们要实现无刷新,一般会使用到AjaxAjax 应用程序可以使用两种基本的方法解决这一问题:一种方法是浏览器每隔若干秒时间向服务器发出轮询以进行更新,另一种方法是服务器始终打开与浏览器的连接并在数据可用时发送给浏览器。第一种方法一般利用setTimeout或是setInterval定时请求,并返回最新数据,这无疑增加了服务器的负担,浪费了大量的资源。而第二种方法也会浪费服务器资源,长期的建立连接;而相对第一种来说,第二种方式会更优于第一种方法;这里有一个一对多和多对一的关系,而comet向多个客户端推送数据就是一对多的关系。而具体使用哪种方式,要看你当前的需求而定,没有绝对的。

 

为什么使用 Comet

轮询方法的主要缺点是:当扩展到更多客户机时,将生成大量的通信量。每个客户机必须定期访问服务器以检查更新,这为服务器资源添加了更多负荷。最坏的一种情况是对不频繁发生更新的应用程序使用轮询,例如一种 Ajax 邮件 Inbox。在这种情况下,相当数量的客户机轮询是没有必要的,服务器对这些轮询的回答只会是 没有产生新数据。虽然可以通过增加轮询的时间间隔来减轻服务器负荷,但是这种方法会产生不良后果,即延迟客户机对服务器事件的感知。当然,很多应用程序可以实现某种权衡,从而获得可接受的轮询方法。

尽管如此,吸引人们使用 Comet 策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量,并且事件发生后可立即发布给客户机。但是保持长期连接处于打开状态也会消耗服务器资源。当等待状态的 servlet 持有一个持久性请求时,该 servlet 会独占一个线程。这将限制 Comet 对传统 servlet 引擎的可伸缩性,因为客户机的数量会很快超过服务器栈能有效处理的线程数量。

 

如果本示例结合Jetty应用服务器效果会更好。

 

开发环境:

SystemWindows

WebBrowserIE6+Firefox3+

JavaEE Servertomcat5.0.2.8tomcat6

IDEeclipseMyEclipse 8

开发依赖库:

JavaEE5Spring 3.0.5dwr 3

Emailhoojo_@126.com

Bloghttp://blog.csdn.net/IBM_hoojo

http://hoojo.cnblogs.com/

 

一、准备工作

1、 下载dwr的相关jar

https://java.net/downloads/dwr/Development%20Builds/Build%20116/dwr.jar

程序中还需要spring的相关jar

http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE

需要的jar包如下

 

 

2、 建立一个WebProject,名称DWRComet

web.xml中添加dwrspring配置如下:

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

        

    <-- dwrcomet控制 -->

    <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配置,这个配置专门配置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>

以上的准备基本完毕,下面来完成无刷新聊天室代码

 

二、聊天室相关业务实现

1、 聊天实体类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;

    //gettersetter

}

 

2、 编写聊天信息的事件

package com.hoo.chat;

 

import org.springframework.context.ApplicationEvent;

 

/**

 * <b>function:</b>发送聊天信息事件

 * @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,构造参数用于传递发送过来的消息。这个事件需要一个监听器监听,一旦触发了这个事件,我们就可以向客户端发送消息。

 

3、 发送消息服务类,用户客户端发送消息。dwr需要暴露这个类里面的发送消息的方法

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>客户端发消息服务类业务

 * @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> 向服务器发送信息,服务器端监听ChatMessageEvent事件,当有事件触发就向所有客户端发送信息

     * @author hoojo

     * @createDate 2011-6-8 下午12:37:24

     * @param msg

     */

    public void sendMessage(Message msg) {

        //发布事件

        ctx.publishEvent(new ChatMessageEvent(msg));

    }

}

上面的sendMessage需要浏览器客户端调用此方法完成消息的发布,传递一个Message对象,并且是触发ChatMessageEvent事件。

 

4、 编写监听器监听客户端是否触发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>监听客户端事件,想客户端推出消息

 * @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就执行下面操作

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

            }

        }

    }

}

上面的代码主要是监听客户端的事件,一旦客户端有触发ApplicationEvent事件或是其子类,就会执行onApplicationEvent方法。代码中通过instanceof判断对象实例,然后再执行。如果有触发ChatMessageEvent事件,就获取所有连接chat.jsp这个页面的ScriptSession。然后像所有的ScriptSession中添加script。这样被添加的ScriptSession就会在有连接chat.jsp的页面中执行。

所以这就是客户端为什么会执行服务器端的JavaScript代码。但前提是需要在web.xml中添加dwrComet配置以及在chat页面添加ajax反转。

 

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>

charServicesendMessage方法传递的是Message对象,所以要配置Message对象的convert配置。

上面的createcreatorspring,表示在spring容器中拿chatService对象。里面的参数的beanName表示在spring容器中找name等于charServicebean对象。

 

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("发送的内容不能为空!");

            }

        }

 

        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 οnlοad="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 " οnclick="send()" style="height85px; width85px;"/>

  </body>

</html>

首先,你需要导入dwrengine.js文件,这个很重要,是dwr的引擎文件。其次你使用的那个类的方法,也需要在导入进来。一般是interface下的,并且在dwr.xml中配置过的create

上面的js中调用的charService类中的sendMessage方法,所以在jsp页面中导入的是ChatService.js

bodyonload事件中,需要设置反转Ajax,这个很重要。

showMessageChatMessageClientonApplicationEvent方法中的appendScript中需要执行的方法。data参数也是在那里传递过来的。

每当发送sendMessage方法后就会触发ChatMessageEvent事件,然后监听的地方就会执行onApplicationEvent方法,在这个方法中又会执行浏览器中的showMessage方法。

 

 

这篇关于Spring整合DWR comet 实现无刷新 多人聊天室的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1028394

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2