1. 简介
DWR(Direct Web Remoting) 是一个 Java 开源框架,一方面可以根据 Java 代码生成相应的 JavaScript 代码供客户端调用,另一方面也可以直接在 Java 代码中调用 JavaScript 代码向客户端推送数据。
# Download URL
http://directwebremoting.org/dwr/downloads/index.html
https://dist.apache.org/repos/dist/release/commons/logging/binaries/
# Documentation URL
http://directwebremoting.org/dwr/documentation/index.html
# 依赖 jar
commons-logging-1.1.3.jar
dwr.jar
2. JavaScript 调用 Java
2.1 编写 Java 代码
package org.demo.dwr.bean;
/**
* bean - User
* @author
* @date 2013-11-20
* @file org.demo.dwr.bean.User.java
*/
public class User {
private String id;
private String name;
private String email;
/**
* toString
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('{');
sb.append("id:" + id).append(", ");
sb.append("name:" + name).append(", ");
sb.append("email:" + email);
sb.append('}');
return sb.toString();
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setEmail(String email) {
this.email = email;
}
}
package org.demo.dwr.web;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.demo.dwr.bean.User;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
/**
* dwr - UserDwr
* @author
* @date 2013-11-20
* @file org.demo.dwr.web.UserDwr.java
*/
public class UserDwr {
private static UserDwr instance = new UserDwr();
private Map<String, User> allUsers = new HashMap<String, User>();
/**
* 当 dwr.xml 中 creator 的类型为 static 时, 需要调用此方法获取一个实例
* @return
*/
public static UserDwr getInstance() {
return instance;
}
/**
* 增加用户
* @param user
* @return
*/
public String addUser(User user) {
allUsers.put(user.getId(), user);
return "success";
}
/**
* 根据 id 查询用户
* @param id
* @return
*/
public String getUser(String id) {
User user = allUsers.get(id);
if (user == null) {
return "User not found - [id: " + id + "]";
}
return user.toString();
}
/**
* 需要访问 request, response, session 等HTTP Servlet对象时只需要在参数列表中声明一下即可
* @param param1
* @param req
* @param resp
* @param session
*/
public void showServletObjects(
String param1,
HttpServletRequest req, HttpServletResponse resp, HttpSession session) {
System.out.println("param1 = " + param1);
System.out.println("request = " + req);
System.out.println("response = " + resp);
System.out.println("session = " + session);
// 也可以通过下面这种方式获取HTTP Servlet对象
WebContext ctx = WebContextFactory.get();
System.out.println("request = " + ctx.getHttpServletRequest());
}
}
2.2 修改 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>DwrTest</display-name>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
2.3 编写 WEB-INF/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>
<!-- 定义需要暴露给前台的 Java 类 -->
<create creator="static" javascript="UserDwr">
<param name="class" value="org.demo.dwr.web.UserDwr" />
</create>
<!-- 定义需要进行转换的 Java Bean -->
<convert converter="bean" match="org.demo.dwr.bean.*" />
</allow>
</dwr>
2.4 编写 index.jsp
<%@ 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 - Test</title>
<!-- 引入与 DWR 相关的 js 文件 -->
<script src='dwr/engine.js'></script>
<script src='dwr/util.js'></script>
<script src='dwr/interface/UserDwr.js'></script>
</head>
<body>
<div>
<input type="button" value="addUser" οnclick="addUser()" />
<input type="button" value="getUser" οnclick="getUser()" />
<input type="button" value="showServletObjects" οnclick="showServletObjects()" />
</div>
<div id="result"></div>
</body>
<script type="text/javascript">
/**
* 增加用户
*/
function addUser() {
var user = {
id : '100',
name : 'zhangsan',
email : 'zhangsan@163.com'
};
UserDwr.addUser(user, {
callback: function(retVal) {
document.getElementById('result').innerHTML += '<br/>' + retVal;
},
timeout: 3000,
errorHandler: function(errorMsg) {
alert('Error Msg : ' + errorMsg);
}
});
}
/**
* 查询用户
*/
function getUser() {
UserDwr.getUser('100', {
callback: function(retVal) {
document.getElementById('result').innerHTML += '<br/>' + retVal;
},
timeout: 3000,
errorHandler: function(errorMsg) {
alert('Error Msg : ' + errorMsg);
}
});
}
/**
* 访问 HTTP Servlet 对象
*/
function showServletObjects() {
UserDwr.showServletObjects('101');
}
</script>
</html>
2.5 测试
3. Java 调用 JavaScript
3.1 编写Java代码
package org.demo.dwr.web;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.extend.ScriptSessionManager;
/**
* dwr - ServerStatusDwr
* @author
* @date 2013-11-20
* @file org.demo.dwr.web.ServerStatusDwr.java
*/
public class ServerStatusDwr implements Runnable {
private static ServerStatusDwr instance = new ServerStatusDwr();
public static ServerStatusDwr getInstance() {
return instance;
}
public ServerStatusDwr() {
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
threadPool.scheduleWithFixedDelay(this, 10, 10, TimeUnit.SECONDS);
}
@Override
public void run() {
ServerContext ctx = ServerContextFactory.get();
if (ctx == null) {
return;
}
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String currentTime = format.format(new Date());
// 在非 DWR 线程中向前台推送数据
ScriptBuffer script = new ScriptBuffer();
script.appendCall("callback_refreshServerTime", currentTime);
ScriptSessionManager manager = ctx.getContainer().getBean(ScriptSessionManager.class);
Iterator<ScriptSession> it = manager.getAllScriptSessions().iterator();
ScriptSession session = null;
while (it.hasNext()) {
session = it.next();
session.addScript(script);
}
}
public void refreshServerTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = format.format(new Date());
// 在 DWR 线程中向前台推送数据
ScriptBuffer script = new ScriptBuffer();
script.appendCall("callback_refreshServerTime", currentTime);
WebContext ctx = WebContextFactory.get();
ScriptSession session = ctx.getScriptSession();
session.addScript(script);
}
public String echo(String value) {
return "echo " + value;
}
}
# 修改 WEB-INF/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>
<!-- 定义需要暴露给前台的 Java 类 -->
<create creator="static" javascript="UserDwr">
<param name="class" value="org.demo.dwr.web.UserDwr" />
</create>
<create creator="static" javascript="ServerStatusDwr">
<param name="class" value="org.demo.dwr.web.ServerStatusDwr" />
<include method="refreshServerTime"/>
<include method="echo"/>
</create>
<!-- 定义需要进行转换的 Java Bean -->
<convert converter="bean" match="org.demo.dwr.bean.*" />
</allow>
</dwr>
# 编写 status.jsp
<%@ 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 - Test</title>
<!-- 引入与 DWR 相关的 js 文件 -->
<script src='dwr/engine.js'></script>
<script src='dwr/util.js'></script>
<script src='dwr/interface/ServerStatusDwr.js'></script>
</head>
<body>
<input type="button" value="echo" οnclick="echo()" />
<input type="button" value="refresh" οnclick="refreshServerTime()" />
<div id="result"></div>
</body>
<script type="text/javascript">
// 采用主动模式时需要去掉下面的注释
// dwr.engine.setActiveReverseAjax(true);
// dwr.engine.setNotifyServerOnPageUnload(true);
function echo() {
ServerStatusDwr.echo('HelloWorld', {
async: false,
callback: function(retVal) {
document.getElementById('result').innerHTML += '<br/>' + retVal;
}
});
}
function refreshServerTime() {
ServerStatusDwr.refreshServerTime();
}
function callback_refreshServerTime(retVal) {
document.getElementById('result').innerHTML += '<br/>' + retVal;
}
</script>
</html>
3.2 被动模式
采用被动模式时,当服务端需要向客户端推送数据时,DWR会先将数据缓存起来,等下次有客户端请求过来时,再将需要推送的数据附加在这次请求的结果中,一起返回给客户端。
采用被动模式时,不需要做什么特殊配置,因为DWR默认就是被动模式。
3.3 主动模式
采用主动模式时,是由客户端主动发起请求从服务端获取需要推送的数据,而不是将这些数据附加在其他请求的结果中进行返回。
# 采用主动模式时需要在 web.xml 中加入参数 activeReverseAjaxEnabled
# 同时在页面中需要加入 dwr.engine.setActiveReverseAjax(true);
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</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>
<script type="text/javascript">
// 采用主动模式时需要去掉下面的注释
dwr.engine.setActiveReverseAjax(true);
dwr.engine.setNotifyServerOnPageUnload(true);
// ...
</script>
4. 同步异步
DWR默认是异步调用,如果要修改成同步调用,有以下两种方法可以选择:
# 修改全局配置,将所有方法的调用模式都修改成同步
dwr.engine.setAsync(false);
# 修改局部配置,将当前方法的调用模式修改成同步
Remote.method(params, {
async: false,
callback: function(retVal){
// ...
}
});
5. 错误处理
DWR中的错误处理也有两种:全局配置和局部配置
# 全局配置
dwr.engine.setErrorHandler(function(errorMsg, exception) {
var info = 'Error: ' + errorMsg
+ ' - Details: ' + dwr.util.toDescriptiveString(exception, 2);
alert(info);
});
# 局部配置
Remote.method(params, {
callback: function(retVal) {
// ...
},
errorHandler: function(errorMsg, exception) {
var info = 'Error: ' + errorMsg
+ ' - Details: ' + dwr.util.toDescriptiveString(exception, 2);
alert(info);
}
});
6. 压缩 js
如果需要将DWR自动生成的 js 文件进行压缩处理,只需要在 WEB-INF/lib 目录中加入yuicompressor-2.4.8.jar 即可
# Download URL
https://github.com/yui/yuicompressor/releases
7. DWR-Spring
7.1 依赖 jar
spring-aop-3.2.5.RELEASE.jar
spring-beans-3.2.5.RELEASE.jar
spring-context-3.2.5.RELEASE.jar
spring-core-3.2.5.RELEASE.jar
spring-expression-3.2.5.RELEASE.jar
spring-web-3.2.5.RELEASE.jar
# Download URL
http://repo.spring.io/simple/libs-release-local/org/springframework/spring/3.2.5.RELEASE/
7.2 修改 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>DwrTest</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
7.3 编写 src/beans.xml
<?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:dwr="http://www.directwebremoting.org/schema/spring-dwr"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd" >
<!-- 自动扫描需要暴露给前台的 Java 类 -->
<!-- Allow DWR to scan the classpath, detect beans annotated with -->
<!-- @RemoteProxy & @RemoteMethod and register the beans and Creator proxies for them. -->
<dwr:annotation-scan base-package="org.demo.dwr.web" scanRemoteProxy="true"/>
<!-- 自动扫描需要进行转换的 Java Bean -->
<dwr:configuration>
<dwr:convert type="bean" class="org.demo.dwr.bean.*" />
</dwr:configuration>
</beans>
7.4 修改 Java 类
# 在需要暴露给前台的 Java 类上添加 @RemoteProxy 注解
# 在需要暴露给前台的 Java 方法上添加 @RemoteMethod 注解
package org.demo.dwr.web;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.demo.dwr.bean.User;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
/**
* dwr - UserDwr
* @author
* @date 2013-11-20
* @file org.demo.dwr.web.UserDwr.java
*/
@RemoteProxy
public class UserDwr {
private Map<String, User> allUsers = new HashMap<String, User>();
/**
* 增加用户
* @param user
* @return
*/
@RemoteMethod
public String addUser(User user) {
allUsers.put(user.getId(), user);
return "success";
}
/**
* 根据 id 查询用户
* @param id
* @return
*/
@RemoteMethod
public String getUser(String id) {
User user = allUsers.get(id);
if (user == null) {
return "User not found - [id: " + id + "]";
}
return user.toString();
}
/**
* 需要访问 request, response, session 等HTTP Servlet对象时只需要在参数列表中声明一下即可
* @param param1
* @param req
* @param resp
* @param session
*/
@RemoteMethod
public void showServletObjects(
String param1,
HttpServletRequest req, HttpServletResponse resp, HttpSession session) {
System.out.println("param1 = " + param1);
System.out.println("request = " + req);
System.out.println("response = " + resp);
System.out.println("session = " + session);
// 也可以通过下面这种方式获取HTTP Servlet对象
WebContext ctx = WebContextFactory.get();
System.out.println("request = " + ctx.getHttpServletRequest());
}
}
package org.demo.dwr.web;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
import org.directwebremoting.extend.ScriptSessionManager;
/**
* dwr - ServerStatusDwr
* @author
* @date 2013-11-20
* @file org.demo.dwr.web.ServerStatusDwr.java
*/
@RemoteProxy
public class ServerStatusDwr implements Runnable {
public ServerStatusDwr() {
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
threadPool.scheduleWithFixedDelay(this, 10, 10, TimeUnit.SECONDS);
}
@Override
public void run() {
ServerContext ctx = ServerContextFactory.get();
if (ctx == null) {
return;
}
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String currentTime = format.format(new Date());
// 在非 DWR 线程中向前台推送数据
ScriptBuffer script = new ScriptBuffer();
script.appendCall("callback_refreshServerTime", currentTime);
ScriptSessionManager manager = ctx.getContainer().getBean(ScriptSessionManager.class);
Iterator<ScriptSession> it = manager.getAllScriptSessions().iterator();
ScriptSession session = null;
while (it.hasNext()) {
session = it.next();
session.addScript(script);
}
}
@RemoteMethod
public void refreshServerTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = format.format(new Date());
// 在 DWR 线程中向前台推送数据
ScriptBuffer script = new ScriptBuffer();
script.appendCall("callback_refreshServerTime", currentTime);
WebContext ctx = WebContextFactory.get();
ScriptSession session = ctx.getScriptSession();
session.addScript(script);
}
@RemoteMethod
public String echo(String value) {
return "echo " + value;
}
}
# DWR上传下载文件
http://directwebremoting.org/dwr-demo/simple/upload.html
http://directwebremoting.org/dwr-demo/simple/download.html
// END