如果不是为了兼容老版浏览器,WebSocket会是最好的选择,虽然网络上很多大咖给了解决方案,

但是感觉实在太吝啬,要嘛发不全,要嘛根本就没解决兼容问题,稳妥起见还是选择DWR……

 

DWR的功能,简单的说就是:

1、在Java代码中调用页面的JS代码,在页面使用JS代码调用后台Java函数;

2、做消息推送的时候,遍历ScriptSession集合,逐个推送消息;

3、在dwr.xml中配置Java类,在页面中引入和Java类同名的Js,即可在页面调用Java代码。

 

使用DWR的时候,页面如果包含DWR相关的JS调用代码,就会创建ScriptSession,通过ScriptSession对象可以实现JS和Java代码之间的相互调用;

值得注意的还有,HttpSession和ScriptSession两者是不同的,先有HttpSession,再有ScriptSession;

 

原理:
后台往前端推送消息:

  DWR通过心跳包的方式不断进行后台访问,一有消息,触发页面的函数,因此,可能出现控制台不断打印日志的情况。

前端调用Java代码:

  如果熟悉Java反射和动态代理的知识,那么就很容易在http协议上,实现远程调用Java类的函数,我们常用的SpringMVC就是这么个东西,我的文章中也实现了这种功能,

而DWR除了远程服务调用,还动态创建了与Java对应的Js脚本,在我们调用Js脚本的时候,向后台提交数据,告知后台需要调用的函数以及参数,通过动态代理,最终调用到我们操作的Java函数。

 

我用的是DWR3的jar包

工具类

import java.util.Collection;

import org.directwebremoting.Browser;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.WebContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * DWR消息推送
 * 
 * @author ChenSS on 2018年4月3日
 */
public class DwrPusher {
    Logger logger = LoggerFactory.getLogger(DwrPusher.class);

    public void sendMsg(String uid, String msg) {
        final String _uid = uid;
        logger.debug("[DWR MESSAGE]: Send Message: " + msg + " User :" + _uid);
        Runnable task = new Runnable() {
            private ScriptBuffer script = new ScriptBuffer();

            public void run() {
                // 设置要调用的 JS及参数
                script.appendCall("show", msg);
                // DWR自身维护了一个Session集,每次刷新浏览器都会重新创建
                // Collection<ScriptSession> sessions = Browser.getTargetSessions();

                // 使用自定义Session集合,优化遍历
                Collection<ScriptSession> sessions = DwrManager.getScriptSessions();
                for (ScriptSession scriptSession : sessions) {
                    if (_uid.equals(scriptSession.getAttribute("uid"))) {
                        scriptSession.addScript(script);
                    }
                }
            }
        };
        // 执行推送
        Browser.withAllSessions(task);
    }

    /**
     * 注册消息推送的Session
     */
    public void onRegister(String uid) {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.setAttribute("uid", uid);
        DwrManager.register(scriptSession);
        logger.debug("[DWR register session uid]: " + uid);
    }
}

推送优化

DWR的ScriptSession容器是无差别保存,自己创建ScriptSession数据容器,专门保存消息接受页面,注意此类需要在web.xml中配置

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.directwebremoting.ScriptSession;
import org.directwebremoting.event.ScriptSessionEvent;
import org.directwebremoting.event.ScriptSessionListener;
import org.directwebremoting.impl.DefaultScriptSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 维护一个消息接受的SessionMap,key为session的Id,value为ScriptSession对象,优化遍历
 * 
 * @author ChenSS on 2018年4月3日
 */
public class DwrManager extends DefaultScriptSessionManager {
    private Logger logger = LoggerFactory.getLogger(DwrManager.class);
    private static Map<String, ScriptSession> pusherMap = new HashMap<>();

    public DwrManager() {
        ScriptSessionListener listener = new ScriptSessionListener() {
            public void sessionCreated(ScriptSessionEvent event) {
                logger.debug("[DWR create session uid]: " + event.getSession().getAttribute("uid"));
            }

            public void sessionDestroyed(ScriptSessionEvent event) {
                pusherMap.remove(event.getSession().getHttpSessionId());
                logger.debug("[DWR destrory session uid]: " + event.getSession().getAttribute("uid"));
            }
        };
        this.addScriptSessionListener(listener);
    }

    public static ScriptSession register(ScriptSession scriptSession) {
        return pusherMap.put(scriptSession.getHttpSessionId(), scriptSession);
    }

    public static Collection<ScriptSession> getScriptSessions() {
        return pusherMap.values();
    }
}

 

Dwr.xml

dwr.xml放在web.xml同级目录,在dwr.xml中注册工具类,消息推送只是一种用途,或许你能想到其他的妙用

<?xml version="1.0" encoding="UTF-8"?>    
<!DOCTYPE dwr PUBLIC    
    "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN"    
    "http://directwebremoting.org/schema/dwr30.dtd">    
<dwr>    
    <allow>    
        <create javascript="DwrPusher" creator="new">    
            <param name="class" value="com.sea.common.util.DwrPusher"></param>    
        </create>    
    </allow>    
</dwr>

Web.xml

<!-- DWR页面消息推送 -->
    <servlet>
        <servlet-name>DWRinvoker</servlet-name>
        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>activeReverseAjaxEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>org.directwebremoting.extend.ScriptSessionManager</param-name>
            <param-value>config.plugin.DwrManager</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DWRinvoker</servlet-name>
        <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>

 

消息接收页面

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    
    <!-- 下面3行是dwr自动生成的Js -->
    <script type="text/javascript" src="${request.contextPath}/dwr/engine.js"></script>
    <script type="text/javascript" src="${request.contextPath}/dwr/util.js"></script>
    <script type="text/javascript" src="${request.contextPath}/dwr/interface/DwrPusher.js"></script>
    
    <script type="text/javascript" src="../../../res/js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            //初始化
            dwr.engine.setActiveReverseAjax(true);
        });
        function onPageLoad() {
            var userId = $("#msg").val();
            DwrPusher.onRegister(userId);
        }
        function show(msg) {
            $("#message").text(msg);
        }
    </script>
    <title>Insert title here</title>
</head>
<body>
<input type="text" id="msg"/>
<input type="button" value="登录" onclick="onPageLoad()"/>

<div id="message"
     style="width: 200px; height: 200px; border: 1px solid red; text-align: center; padding: 5px;"></div>
</body>
</html>

页面推送消息

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    
    <!-- 下面3行是dwr自动生成的Js -->
    <script type="text/javascript" src="${request.contextPath}/dwr/engine.js"></script>
    <script type="text/javascript" src="${request.contextPath}/dwr/util.js"></script>
    <script type="text/javascript" src="${request.contextPath}/dwr/interface/DwrPusher.js"></script>
    
    <script type="text/javascript" src="../../../res/js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            //初始化
            dwr.engine.setActiveReverseAjax(true);
            
            $("#but").click(function () {
                DwrPusher.sendMsg(30, $("#msg").val());
            });
        });
    </script>
    <title>Insert title here</title>
</head>
<body>
<input type="text" id="msg"/>
<input type="button" value="发送" id="but"/>
</body>
</html>