DWR——Web消息推送
如果不是为了兼容老版浏览器,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>