一、技术原理
DWR是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开发人员开发包含AJAX技术的网站。
它可以允许在浏览器里的代码使用运行在WEB服务器上的JAVA函数,就像它就在浏览器里一样。
它包含两个主要的部分:允许JavaScript从WEB服务器上一个遵循了AJAX原则的Servlet中获取数据。
另外一方面一个JavaScript库可以帮助网站开发人员轻松地利用获取的数据来动态改变网页的内容。
官方网址:DWR官方网址
二、应用场景
使用本技术,可以实现即使聊天,消息实时推送,实时动态网页等功能
三、现有方式
参考现有聊天工具,当我们有消息时,聊天工具的图标会跳动,提醒我们有未读消息可以查看,这种功能在C/S模式下很容易实现,但是如果要在B/S模式如何来实现呢。
我们的肯定会想到在客服端调用ajax在后台不断的查询服务器.看是否有关于自己的消息,如果有则查询返回。
这种做法肯定会大量的占用系统的资源,是一种效率比较低的做法,不可取。
而现在DWR的反转AJAX功能,允许我们从服务器端来控制客服端,不需要客户端请求,服务器就可以自动把消息发给指定的客户端。
(以上文字内容参考https://blog.csdn.net/wendychiang1991/article/details/52857943 )
三、代码实现
项目结构
1.jar包下载 dwr.jar
或maven依赖:
<dependency>
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
<version>3.0.2-RELEASE</version>
</dependency>
2.web.xml中增加servlet拦截
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>initApplicationScopeCreatorsAtStartup</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>jsonRpcEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>jsonpEnabled</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
3.新建一个dwr的配置文件 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>
<allow>
<!-- javascript对应前台引入的interface/MessagePusher.js文件名称 -->
<create creator="new" javascript="MessagePusher">
<param name="class" value="top.zhoudl.dwr.MessagePusher"/>
</create>
</allow>
</dwr>
4.上述配置文件中配置了 top.zhoudl.dwr.MessagePusher 这个对象,所以实现这个对象
/**
* @author zhoudongliang
* @create 2018/7/17
**/
public class MessagePusher {
final public static String SCRIPT_SESSION_USERID = "SCRIPT_SESSION_USERID";
final public static String SCRIPT_SESSION_MSG = "showMessage";
//这是页面上当后台消息推送时,自动触发的js方法名称
public void onPageLoad(String userId) {
if (StringUtils.isNotBlank(userId)){
ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
scriptSession.setAttribute("userId", userId);
System.out.println("添加了一个scriptSession: "+userId);
}
/*DwrScriptSessionManagerUtil dwrScriptSessionManagerUtil = new DwrScriptSessionManagerUtil();
try {
dwrScriptSessionManagerUtil.init();
} catch (ServletException e) {
e.printStackTrace();
}*/
}
/**
* 采用dwr的方式向前台推送消息
* @param userid 用户Id
* @param message 消息内容
*/
public void sendMessage(final String userid, final String message) {
final String id = userid;
final String msg = message;
Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
//如果返回true,那将匹配的ScriptSession添加到Browser.getTargetSessions()中,待后面调用
public boolean match(ScriptSession session) {
String userId = (String) session.getAttribute("userId");
if (userId == null) {
return false;
} else if ("0".equalsIgnoreCase(id)) {
return true;
} else {
return userId.equalsIgnoreCase(id);
}
}
}, new Runnable() {
private ScriptBuffer script = new ScriptBuffer();
@Override
public void run() {
//对应页面调用的方法名称及参数
script.appendCall(SCRIPT_SESSION_MSG, msg);
Collection<ScriptSession> sessions = Browser.getTargetSessions();
for (ScriptSession scriptSession : sessions) {
scriptSession.addScript(script);
}
}
});
}
}
5.初始化监听对象 DwrScriptSessionManagerUtil
/**
* @author zhoudongliang
* @create 2018/7/17
**/
public class DwrScriptSessionManagerUtil extends DwrServlet {
/** */
private static final long serialVersionUID = -8778373469311495523L;
/**
* 初始化dwr监听,只在程序中调用一次即可
*/
@Override
public void init() throws ServletException {
Container container = ServerContextFactory.get().getContainer();
ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);
ScriptSessionListener listener = new ScriptSessionListener() {
@Override
public void sessionCreated(ScriptSessionEvent ev) {
//从session中获取用户
HttpSession session = WebContextFactory.get().getSession();
User user = ((User) session.getAttribute("user"));
if (user != null) {
String userId = user.getUserId();
System.out.println("创建了一个新的ScriptSession: "+userId);
ev.getSession().setAttribute("userId", userId);
}
}
@Override
public void sessionDestroyed(ScriptSessionEvent ev) {
System.out.println("销毁了一个ScriptSession");
}
};
manager.addScriptSessionListener(listener);
}
}
至此,后台代码基本技术,接下来看前台代码
6.引入js文件
<!-- 路径从web.xml中配置,具体文件不需要特意引入真实的js,项目自动生成 -->
<!-- DwrDemo自动生成,名称对应dwr中配置的create:javascript,路径对应web.xml中配置的url-pattern -->
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/engine.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/util.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/interface/MessagePusher.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/js/jquery.js'></script>
7.几种不同的消息发送方式
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>DWR消息推送--发送</title>
<!-- 路径从web.xml中配置,具体文件不需要特意引入真实的js,项目自动生成 -->
<!-- DwrDemo自动生成,名称对应dwr中配置的create:javascript,路径对应web.xml中配置的url-pattern -->
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/engine.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/util.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/interface/MessagePusher.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/js/jquery.js'></script>
<script type="text/javascript">
function init(){
dwr.engine.setActiveReverseAjax(true);
dwr.engine.setNotifyServerOnPageUnload(true);
//DwrDemo.onPageLoad();发送页面可以不需要添加ScriptSession
}
//页面加载时调用init函数
window.onload = init;
//通过dwr直接调用
function send(userId){
var msg = $("#msg").val();
//sendMessage对应后台dwr.xml中对应param的类的方法top.zhoudl.dwr.MessagePusher.sendMessage
if (!userId || !msg) return;
MessagePusher.sendMessage(userId,msg);
}
//通过ajax调用
function update() {
$.ajax({
type:'post',
contentType:'application/json',
date:{"userId":"0"},
url:'<%=request.getContextPath() %>/web/update.do',
success:function(data){
if(date.SUCCESS == '200'){
alert("更新并发送消息成功!");
}
}
})
}
</script>
</head>
<body>
<textarea id="msg" rows="10" cols="70"></textarea></br>
<button onclick="send('0');">给所有人发送</button>
<button onclick="send(1);">给人员1发送</button>
<button onclick="send(2);">给人员2发送</button>
<button onclick="update();">Ajax过程中发送</button></br></br>
<span>通过访问页面:项目名称/web/dwrReceiveMsg.do?userId=2(人员假定id)接收消息</span>
</body>
</html>
8.消息接收方式
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>DWR消息推送--接收</title>
<!-- 路径从web.xml中配置,具体文件不需要特意引入真实的js,项目自动生成 -->
<!-- DwrDemo自动生成,名称对应dwr中配置的create:javascript,路径对应web.xml中配置的url-pattern -->
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/engine.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/util.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/dwr/interface/MessagePusher.js'></script>
<script type="text/javascript" src='<%=request.getContextPath() %>/js/jquery.js'></script>
<script type="text/javascript">
var userId = '${param.userId}';
function init(){
if (userId || userId != ''){
dwr.engine.setActiveReverseAjax(true);
dwr.engine.setNotifyServerOnPageUnload(true);
MessagePusher.onPageLoad(userId);
}
}
//页面加载时调用init函数
window.onload = init;
//对应appendCall设置的方法名称
function showMessage(msg) {
$("#msg").val(msg);
alert(msg);
}
</script>
</head>
<body>
<span>切换用户时可以在url后添加userId参数及其值</span></br>
<span>当前用户id: ${param.userId }</span></br>
<textarea id="msg" rows="10" cols="70"></textarea>
</body>
</html>
测试结果(使用了不同浏览器进行测试)
首页
http://localhost:8080/
消息发送地址
http://localhost:8080/web/dwrSendMsg.do
接收消息地址 userId 为参数
http://localhost:8080/web/dwrReceiveMsg.do?userId=1
发送消息
以上案例详细代码位于:demo