一个非常好的DWR实例

DWR(Direct Web Remoting) 是一个WEB 远程调用框架. 利用这个框架可以让AJAX 开发变得很简单. 利用DWR 可以在客户端利用JavaScript 直接调用服务端的Java 方法并返回值给JavaScript 就好像直接本地客户端调用一样(DWR 根据Java 类来动态生成JavaScrip 代码).

DWR 确实是一个非常优秀的项目,它通过反射,将java 翻译成javascript ,然后利用回调机制,轻松实现了javascript 调用Java 代码。

其大概开发过程如下:
1. 编写业务代码,该代码是和dwr 无关的。
2. 确认业务代码中哪些类、哪些方法是要由javascript 直接访问的。
3. 编写dwr 组件,对步骤2 的方法进行封装。
4. 配置dwr 组件到dwr.xml 文件中, 如果有必要,配置convert ,进行java 和javascript 类型互转。
5. 通过反射机制,dwr 将步骤4 的类转换成javascript 代码,提供给前台页面调用。
5. 编写网页,调用步骤5 的javascript 中的相关方法(间接调用服务器端的相关类的方法),执行业务逻辑,将执行结果利用回调函数返回。
6. 在回调函数中,得到执行结果后,可以继续编写业务逻辑的相关javascript 代码。

下面以用户注册的例子,来说明其使用。(注意,本次例子只是用于演示,说明DWR 的使用,类设计并不是最优的)。

1. 先介绍下相关的Java 类

    User: 用户类,
    public class User {
//
登陆ID ,主键唯一
private String id;
//
姓名
private String name;
//
口令
private String password;
//
电子邮件
private String email;
        
//
以下包含getXXX 和setXXX 方法
.......
    }

    UserDAO
:实现User 的数据库访问,这里作为一个演示,编写测试代码
    public class UserDAO {
      //
存放保存的数据
      private static Map dataMap = new HashMap();

      //
持久用户
      public boolean save(User user) {
        if (dataMap.containsKey(user.getId()))
          return false;
        System.out.println("
下面开始保存用户 ");
        System.out.println("id :
"+user.getId());
        System.out.println("password :
"+user.getPassword());
        System.out.println("name :
"+user.getName());
        System.out.println("email :
"+user.getEmail());
        dataMap.put(user.getId(), user);
        System.out.println(" 用户保存结束
");
        return true;
      }

      // 查找用户

      public User find(String id) {
        return (User)dataMap.get(id);
      }
}

    DWRUserAccess
:DWR 组件,提供给javascript 访问的。

    public class DWRUserAccess {

        UserDAO userDAO = new UserDAO();

        public boolean save(User user) {
          return userDAO.save(user);
        }

        public User find(String id) {
          return userDAO.find(id);
        }
    }
  

   
下面说明下程序执行的流程

    1. 用户在页面上输入相关注册信息,id 、name 、password 、email ,点击“ 提交” 按钮
    2.javascript 代码开始执行,根据用户填写相关信息,通过dwr 提供的DWRUserAccess.js 里save 的方法,调用服务器端的DWRUserAccess 类save 方法,将注册信息保存。
    3. 通过DWRUserAccess.jsp 里的find 方法,调用服务器端DWRUserAccess 类里的find 方法,执行用户信息查找。

    注意,在以上的执行过程中,DWRUserAccess 是供DWR 调用的,是DWR 组件,因此需要将DWRUserAccess 类配置到dwr 中。

    接下来讲解本次dwr 测试环境的配置。

    1. 新建一个webapp ,命名为 testApp
    2. 将dwr.jar 拷贝到testApp 的WEB-INF 的lib 目录下

    3. 编译上面的User ,UserDAO ,DWRUserAccess 类,放到classes 目录下
    4. 在web.xml 中配置servlet, 适配路径到dwr 目录下, 如下所示
      <servlet>
      <servlet-name>dwr-invoker</servlet-name>
      <!--
      <display-name>DWR Servlet</display-name>
      <description>Direct Web Remoter Servlet</description>
      --!>
      <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
      <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value>
      </init-param>
      <init-param>
        <param-name>scriptCompressed</param-name>
        <param-value>false</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>

   
以上的配置可以拦截testApp 下所有指向dwr 的请求,关于这个拦截器,我们会在后面介绍。

    5.WEB-INF 下新建一个dwr.xml 文件,内容如下:
    < xml version="1.0" encoding="UTF-8" >
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd">

<dwr>
    <allow>
<create creator="new" javascript="DWRUserAccess">
        <param name="class" value="test.DWRUserAccess"/>
      </create>
<convert converter="bean" match="test.User"/>
    </allow>
</dwr>

   
这里我们把DWRUserAccess 配置到了dwr 中,create 元素中,creater="new" 表示每调用一次DWRUserAccess 时,需要new 一个这样的类;javascript="DWRUserAccess" ,表示提供给前台页面调用的javascirpt 文件是DWRUserAccess.js 。

    convert 元素用于数据类型转换,即java 类和javascript 之间相互转换,因为和前台交换的是User 对象,因此需要对此使用bean 转换,我们将在后面介绍这个类。

    4. 编写测试的HTML 页面 test.html
     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>DWR 测试
</TITLE>
<meta http-equiv=Content-Type content="text/html; charset=gb2312">
<script src="/oblog312/dwr/engine.js"></script>
<script src="/oblog312/dwr/util.js"></script>
<script src="/oblog312/dwr/interface/DWRUserAccess.js"></script>
</HEAD>
<BODY>
<B> 用户注册
</B><br>
------------------------------------------------
<Br>
<form name="regForm">
登陆ID :
<input type="text" name="id"><br>
口    令:
<input type="password" name="password"><br>
姓    名:
<input type="text" name="name"><br>
电子邮件:
<input type="text" name="email"><br>
<input type="button" name="submitBtn" value=" 提交
" οnclick="OnSave()"><br>
      </form>

<br>
<br><B> 用户查询
</B><br>
------------------------------------------------
<Br>
<form name="queryForm">
登陆ID :
<input type="text" name="id"><br>
<input type="button" name="submitBtn" value=" 提交
" οnclick="OnFind()"><br>
</form>
<br>
</BODY>
</HTML>
<SCRIPT LANGUAGE="JavaScript">
<!--
function saveFun(data) {
if (data) {
    alert(" 注册成功!
");
} else {
    alert(" 登陆ID 已经存在!
");
}
}

function OnSave() {
var userMap = {};
userMap.id = regForm.id.value;
userMap.password = regForm.password.value;
userMap.name = regForm.name.value;
userMap.email = regForm.email.value;
DWRUserAccess.save(userMap, saveFun);
}

function findFun(data) {
if (data == null) {
    alert(" 无法找到用户:
"+queryForm.id.value);
    return;
}

alert(" 找到用户,nid :"+data.id+" ,npassword :"+data.password+" ,nname :"+data.name+" ,nemail :
"+data.email);

}

function OnFind() {
DWRUserAccess.find(queryForm.id.value, findFun);
}
//-->
</SCRIPT>


以下对页面的javascript 进行解释


<script src="/oblog312/dwr/engine.js"></script>
<script src="/oblog312/dwr/util.js"></script>
这两个是dwr 提供的,用户可以不必关心,只需要导入即可

<script src="/oblog312/dwr/interface/DWRUserAccess.js"></script>
是我们编写的DWRUserAccess 类,经dwr 反射后,生成的javascript 代码,它和DWRUserAccess.java 是对应的,供用户调用,实际上我们就是通过这个js 文件去调用服务器端的DWRUserAccess 类的。

<SCRIPT LANGUAGE="JavaScript">
<!--
function saveFun(data) {
if (data) {
    alert("
注册成功! ");
} else {
    alert(" 用户名已经存在!
");
}
}

function OnSave() {
var userMap = {};
userMap.id = regForm.id.value;
userMap.password = regForm.password.value;
userMap.name = regForm.name.value;
userMap.email = regForm.email.value;
DWRUserAccess.save(userMap, saveFun);
}

function findFun(data) {
if (data == null) {
    alert(" 无法找到用户:
"+queryForm.id.value);
    return;
}

alert(" 找到用户,nid :"+data.id+" ,npassword :"+data.password+" ,nname :"+data.name+" ,nemail :
"+data.email);

}

function OnFind() {
DWRUserAccess.find(queryForm.id.value, findFun);
}
//-->
</SCRIPT>

这段javascirpt 代码,我们来看下OnSave 函数,首先它构造一个map ,将表单数据都设置到map 中,然后调用DWRUserAccess.save(userMap, saveFun) ,执行save 操作。大家可以注意到,服务器端的DWRUserAccess 中的save 方法是这样的:boolean save(User user) ,其参数是一个User 对象,返回一个boolean 值;而客户端的方法是这样的:save(userMap,saveFun) ,第一个参数userMap 是javascirpt 中的map 对象,在这里相当于服务器端的User 对象(在服务器端执行时,会通过convert 转换成User 对象),前面我们提到dwr 是利用回调函数来返回执行结果的,第二个参数saveFun 即是一个回调函数。在函数function saveFun(data) 中,data 是执行结果,这里是一个bool 值,非常简单的,我们通过判断data 是否为真,可以知道用户名是否重复,用户是否注册成功。


看一下OnFind 查找函数,执行结果在回调函数findFun(data) 中,因为服务器端返回的是一个User 对象,通过convert ,将会转换成javascript 的一个map 对象,
于是在findFun 中,通过data.id 、data.name 、data.password 、data.email 我们可以轻松的访问到这个User 对象。


好了配置完毕,启动服务器,在目录中打入localhost/testApp/test.html 。

1. 在“ 用户注册” 表单中,id 框中输入admin ,password 中输入123456 ,name 中输入chenbug ,email 中输入chenbug@zj.com ,点击提交按钮,弹出对话框:“ 注册成功” ,在服务器后台可以看到信息如下:

下面开始保存用户
id admin
password :
123456
name :
chenbug
email :
chenbug@zj.com
用户保存结束


再次点击提交按钮,弹出对话框“ 登陆ID 已经存在” 。

2. 在“ 用户查询” 对话框中,输入登陆ID 为admin ,点击提交按钮,提示找到用户,并显示相关信息,输入admin123 ,点击提交按钮,提示无法找到用户。

至此,测试结束。


后续:
1 。拦截器 uk.ltd.getahead.dwr.DWRServlet
该类拦截所有指向dwr 目录下的请求,并调用Processor 的handler 方法进行处理,在uk.ltd.getahead.dwr.impl.DefaultProcessor 下,我们可以看到详细的处理过程。

if (pathInfo.length() == 0 ||
              pathInfo.equals(HtmlConstants.PATH_ROOT) ||
              pathInfo.equals(req.getContextPath()))
          {
              resp.sendRedirect(req.getContextPath() + servletPath + HtmlConstants.FILE_INDEX);
          }
          else if (pathInfo.startsWith(HtmlConstants.FILE_INDEX))
          {
              index.handle(req, resp);
          }
          else if (pathInfo.startsWith(HtmlConstants.PATH_TEST))
          {
              test.handle(req, resp);
          }
          else if (pathInfo.startsWith(HtmlConstants.PATH_INTERFACE))
          {
              iface.handle(req, resp);
          }
          else if (pathInfo.startsWith(HtmlConstants.PATH_EXEC))
          {
              exec.handle(req, resp);
          }
          else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_ENGINE))
          {
              file.doFile(req, resp, HtmlConstants.FILE_ENGINE, HtmlConstants.MIME_JS);
          }
          else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_UTIL))
          {
              file.doFile(req, resp, HtmlConstants.FILE_UTIL, HtmlConstants.MIME_JS);
          }
          else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_DEPRECATED))
          {
              file.doFile(req, resp, HtmlConstants.FILE_DEPRECATED, HtmlConstants.MIME_JS);
          }
          else
          {
              log.warn("Page not found (" + pathInfo + "). In debug/test mode try viewing /[WEB-APP]/dwr/"); //$NON-NLS-1$ //$NON-NLS-2$
              resp.sendError(HttpServletResponse.SC_NOT_FOUND);
          }

通过判断request 请求的servlet 路径,进行处理,大家可以自己去参看,这里不详细讨论。


2.bean 转换器, <convert converter="bean" match="test.User"/>
将dwr.jar 解压缩,在路径ukltdgetaheaddwr 下可以看到dwr.xml ,这里配置了系统默认的一些转换器,

<converter id="bean" class="uk.ltd.getahead.dwr.convert.BeanConverter"/> 即是刚才用到User 类的转换器,进入代码我们来看看它是如何在javascript 和java 间进行转换的。

打开BeanConverter 代码,定位到函数

public Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx) throws ConversionException

即是将javascript 对象转换成java 对象的,其中
paramType 即Class 类型,在上面的例子中是test.User ,
InboundVariable iv ,是传入的值,通过iv.getValue 可以得到传入的javascript 值串
InboundContext inctx ,是入口参数上下文,用于保存转换的后java 对象。

因为前台传入的是一个javascript 的map 类型,而map 肯定是以{ 开始和以} 结束的,于是在这个函数一开始进行了判断
if (!value.startsWith(ConversionConstants.INBOUND_MAP_START))
          {
              throw new IllegalArgumentException(Messages.getString("BeanConverter.MissingOpener", ConversionConstants.INBOUND_MAP_START)); //$NON-NLS-1$
          }

          if (!value.endsWith(ConversionConstants.INBOUND_MAP_END))
          {
              throw new IllegalArgumentException(Messages.getString("BeanConverter.MissingCloser", ConversionConstants.INBOUND_MAP_START)); //$NON-NLS-1$
          }

javascript
中,map 里各个项是用逗号连接的,如var userMap = {id:'admin',password:'123456',name:'chenbug',email:'chenbug@zj.com'}; 而每个项的键值对是用冒号连接的,
在convertInbound 函数的接下来的处理中,即是通过分析map 字串,通过paramType 构造java 实例(即User 类),然后通过反射,将这些键值对设置到java 实例中,并返回。
这样就完成了javascript 到java 的转换。


另一个函数
public String convertOutbound(Object data, String varname, OutboundContext outctx) throws ConversionException

即是将java 对象转换为javascript 对象(其实是声明和赋值语句)。
Object data ,是待转换的java 对象
String varname ,是javascript 中的该对象的变量名
OutboundContext outctx ,传出参数上下文,用于保存转换后的javascript 值

StringBuffer buffer = new StringBuffer();
          buffer.append("var "); //$NON-NLS-1$
          buffer.append(varname);
          buffer.append("={};"); //$NON-NLS-1$
这里声明了map 类型的变量。

即下来来的代码即是通过反射进行变量赋值,如下
    buffer.append(varname);
                      buffer.append('.');
                      buffer.append(name);
                      buffer.append('=');
                      buffer.append(nested.getAssignCode());
                      buffer.append(';');
大家可以自己去参看更多的代码。

3.dwr 本身提供了一个测试环境,大家在配置完后,可以在IE 中输入地址http://localhost/testApp/dwr/index.html ,看到配置的各DWR 组件,并进行相关测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值