如果不是为了兼容老版浏览器,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>