如何在Spring Web MVC环境下使用DWR?

日期: 2011-01-23 作者:高乐怡 来源:TechTarget中国 英文

  目前Ajax的开发框架有很多,使用这些框架可以简化Ajax的开发。DWR (Direct Web Remoting)是一个用于改善Web页面与Java类交互的远程服务器端Ajax开源框架。DWR可以动态生成基于Java类的JavaScript代码。对于公开的每个类,DWR帮我们做好了创建对象、发送数据、接受响应等许多繁琐的工作,大大节省了客户端代码和工作量。

  示例应用程序:用户注册

  本文使用的示例应用程序是一个简单的用户注册过程。借助简化了的数据模型,主要介绍DWR和Spring MVC的结合。我们先来研究一下DWR如何启用程序的Ajax功能。

  配置DWR

  首先,需要下载dwr.jar文件,把它放在web程序的WEB-INF/lib目录下面。清单 1 显示了要公开给Ajax的Java方法。

  清单 1. 需要公开的userManager类的两个方法
    

以下是引用片段:
 public int findExistedUser(String name) 
 public List<User> showUsers() 
 
  接下来需要配置DWR,告诉它Ajax应当如何构建userManager并调用这些方法。在清单 2 所示的dwr.xml文件中进行配置。

  清单 2. 配置 DWR,公开需要远程调用的方法
    

以下是引用片段:
 <?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=”spring” javascript=”userManager” scope=”application”> 
 <param name=”beanName” value=”userManager”/> 
 <include method=”findExistedUser”/> 
 <include method=”showAllUsers”/> 
 </create> 
 <convert converter=”bean” match=”com.ibm.osl.entity.User”> 
 <param name=”include” value=”name”/> 
 </convert> 
 </allow> 
 </dwr> 

  creator属性用来指定使用哪种创造器。如果creator属性被设置为值new,意味着DWR调用类的默认构造函数来获得实例。这里通过与Spring进行集成来获得实例,creator=”spring”提供了一个Spring的创造器,允许直接调用Spring容器中的bean,然后DWR将bean转换成一个 javascript对象。SpringCreator 这个创造器会查找Spring所配置的bean,并创建它们。Javascript属性用于指定浏览器中被创造出来的对象的名字。scope属性指定这个bean的生命周期。嵌套在create元素内的 param 元素的name属性值可以是class,beanName等。此处用 beanName,value 的值 userManager 是在beans.xml中定义的某个id值。include元素指定公开的方法名称。也可以用exclude元素指定不想被被访问的方法。

  convert元素的作用是告诉 DWR 在服务器端的 Java 对象表示和 JavaScript 之间如何转换数据类型。

  DWR能自动地在Java对象和JavaScript表示之间转换简单数据类型。这些类型包括Java原生类型和它们各自的类表示,还有数组和集合类型。这里convert元素告诉DWR用bean转换器处理showAllUsers方法返回的List<User>对象,并指定序列化中只包含User类的name属性。

  接下来在清单 3 中定义需要的bean 。

  清单 3. 配置beans.xml
    

以下是引用片段:
 <beans> 
    <bean id=”userManager” class=”developworks.dwrspring.service.impl.UserManagerImpl”/> 
 </beans> 

  有三种方式寻找配置文件beans.xml,最简单的方式是使用org.springframework.web.context.ContextLoaderListener。需要在web.xml中做如下配置:

  清单 4. 使用ContextLoaderListener寻找配置文件beans.xml
    

以下是引用片段:
 <context-param> 
 <param-name>contextConfigLocation</param-name> 
 <param-value>/WEB-INF/beans.xml</param-value> 
 </context-param> 
 <listener> 
 <listener-class> 
 org.springframework.web.context.ContextLoaderListener 
 </listener-class> 
 </listener> 

 
  测试部署

  如果在web.xml中把init-param的debug属性设置为true,就会启用DWR的测试模式,如清单 5 所示。访问/[web-app]/dwr/就可以看到服务器暴露出来的所有类列表。


  清单 5. 将web.xml的debug属性设置为true
    

以下是引用片段:
 <init-param> 
 <param-name>debug</param-name> 
 <param-value>true</param-value> 
 </init-param> 

  清单 6. 服务器端暴露出来的所有类列表
    

以下是引用片段:
Classes known to DWR: 
    * userManager (developworks.dwrspring.service.impl.UserManagerImpl) 

 
  点击进入userManager可以看到该类暴露出的所有方法,并可以进行测试。这里有两个用户自己定义的方法,可以在可访问的方法旁边的文本框中输入参数值并点击Execute按钮调用方法。服务器的响应如果是简单值,会在方法旁边直接显示。其他的输出将在警告框中用JSON标注显示出来。这样,测试页面不仅可以检查公开了哪个类和方法用于远程调用,还可以测试每个方法是否正常工作。下面让我们看下本例中的两个方法:

  函数findExistedUser(“”)可以查找是否存在该用户。若不输入参数,点击Execute按钮,显示0。在函数参数中输入已存在的id,如 “tom@cn.ibm.com”,点击Execute按钮,显示 1。

  函数showAllUsers()可以显示所有的用户名。点击Execute按钮,会弹出一个显示所有user信息的警告框,内容如清单 7。

  清单 7. 点击Execute按钮返回的JSON对象
    

以下是引用片段:
 [ 
 {name:”tom@cn.ibm.com”}, 
 {name:”mike@cn.ibm.com”} 
 ] 

  调用远程对象

  对于公开的每个类,DWR动态地生成包含在Web页面中的JavaScript。生成的JavaScript 包含存根函数,代表Java类对应的方法。Java方法与对应的JavaScript函数之间的映射规则为JavaScriptName.methodName(methodParams …, callBack),其中JavaScriptName是creator属性指定浏览器中被创造出来的对象名字,methodName是Java的方法名,methodParams代表Java方法的参数,最后的callBack是Java方法返回后要回调的JavaScript函数。清单 8 是在jsp页面中使用DWR创建的javascript对象调用公开的方法。

  清单 8. 用javascript对象调用公开的方法
    

以下是引用片段:
 function callBackfindExistedUser(data){ 
 var userExistedMessage = $(“userExistedMessage”); 
 if (data == 1) 
 userExistedMessage.style.display = “block”; 
 else 
 userExistedMessage.style.display = “none”; 
 } 
 function findExistedUser(){ 
 var name = DWRUtil.getValue(“name”); 
 if (name != “”) 
 userManager.findExistedUser(name,callBackfindExistedUser); 
 } 

  在jsp页面使用DWR,需要添加清单 9 的代码。

  清单 9. 在jsp页面使用DWR要包含的文件
    

以下是引用片段:
 <script type=’text/javascript’ src=’dwr/interface/userManager.js’></script> 
 <script type=’text/javascript’ src=’dwr/engine.js’></script> 
 <script type=’text/javascript’ src=’dwr/util.js’></script> 

  DWR会自动根据UserManager类帮我们生成了userManager.js 文件。engine.js是用来转换动态生成接口的javascript函数调用,util.js包含了一些工具函数,让页面对javascript的调用更加方便。

  Spring MVC简介

  Spring框架提供了构建Web应用程序的MVC模块。Spring MVC内建了一个请求驱动的web mvc框架,以一个DispatcherServlet分发器为中心,将web请求分发到各个不同的处理器进行处理。Spring MVC分离了控制器、模型对象、分派器以及处理程序对象的角色。

  配置Spring MVC

  需要下载spring.jar和spring-webmvc.jar包,将它放 web程序的WEB-INF/lib目录下面。

  本例中使用控制器AbstractWizardFormController,它的主要功能是允许多个表单共用一个表单对象,通常一些表单填的内容比较多,表单会很长,可以将表单分成几页,让用户一页页去完成。表单对象的属性与多个页面所有需填写的对象绑定。AbstractWizardFormController类的 processFinish方法定义的是全部表单完成后提交的处理。所有的表单内容都收集在command对象中,通过ModelAndView的构造方法把它放在model对象中,并在 view 页面上显示出来。

  清单 10. processFinish方法将表单内容传输给view页面
    

以下是引用片段:
 protected ModelAndView processFinish(HttpServletRequest request, 
 HttpServletResponse response, 
 Object command, 
 BindException exception) throws Exception { 
 RegisterForm registerForm = (RegisterForm) command; 
 return new ModelAndView(this.getSuccessView(), “registerForm”, registerForm); 
 } 

  清单 11 在mvc-config.xml中对controller进行配置。

  清单 11. 对controller进行配置
    

以下是引用片段:
 <bean id=”registerController” class=” developworks.dwrspring.action.RegisterController”> 
    <property name=”commandClass” value=”developworks.dwrspring.vo.RegisterForm”> 
 </property> 
 <property name=”userManager” ref=”userManager”></property> 
 <property name=”successView” value=”account/success”/> 
 <property name=”pages”> 
 <list> 
 <value>account/register1</value> 
 <value>account/register2</value> 
 </list> 
 </property> 
 </bean> 

  这里commandClass用来绑定form的bean class。list元素定义的两个value值分别表示表单的第一个页面和第二个页面。successView属性表示表单提交后要转到的页面。

  第一个页面中的表单内容如下:

  清单 12. 第一个表单页面
    

以下是引用片段:
 <form:form action=”register.action” method=”post”> 
 <form:input path=”name” onblur=”javascript:findExistedUser()”/> 
 <form:input path=”age”/> 
 <input type=”submit” value=”Next” name=”_target1″ /> 
 </form:form> 
 
  path 中的属性name对应的是表单对象RegisterForm的name属性。Javascript方法findExistedUser会调用DWR产生的javascript对象的findExistedUser方法来判断是否存在该用户。
  target1的数字 1 表示在xml文件中 <list> 的顺序。这里_target1对应的是account/register2页面。即提交后转到第二个表单页面继续填写。
  第二个页面中的表单内容如下:
  清单 13. 第二个表单页面
    
 <form:form action=”register.action” method=”post”> 
 <form:select path=”location”> 
 <form:option value=”” label=”—“/> 
 <form:options items=”${location}”/> 
 </form:select> 
 <input class=”button” type=”submit” value=”Back” name=”_target0″/> 
 <input class=”button” type=”submit” value=”Submit” name=”_finish” /> 
 </form:form> 

 
  _target0对应的是account/register1页面。即回到前一个表单页面。
  
  _finish会执行controller的processFinish 法。

  path中的属性location所含的数据来自于controller的referenceData函数为表单页面准备的数据。

  清单 14. 为页面的select框准备数据
    

以下是引用片段:
 protected Map<String,Object> referenceData(HttpServletRequest request, int page) 
     throws Exception { 
 refData = new HashMap<String,Object>(); 
 if (page == 1){ 
 Map<String,Object> location = new HashMap<String,Object>(); 
 location.put(“0”, “Beijing”); 
 location.put(“1”, “Shanghai”); 
 refData.put(“location”, location); 
 } 
 return refData; 
 } 

  referenceData函数为第二个页面register2.jsp的select框的location下拉框添加选项。

  最后在success页面通过 <c:out value=”${registerForm.name}” /> 等jstl标签显示先前表单输入的值。用户还可以通过点击按钮showAllUsers来显示当前所有用户。因为在convert属性中只配置了name属性,所以用户只能看到所有的用户名。若输出age属性,显示 undefined。

  清单 15. 在页面中显示所有的用户名
    

以下是引用片段:
 function showAllUser(){ 
    userManager.showUsers(callBackAllUser); 
 } 
 function callBackAllUser(data){ 
    var result = “”; 
    for (var i = 0; i < data.length; i++){ 
        result += data[i].name + “, “; 
    } 
    DWRUtil.setValue(“demoAllUser”,result); 
 } 

  使用DWR标注

  DWR从 2.0版本开始支持标注,需要在JDK1.5以上使用。使用标注可以省掉dwr.xml,把这个文件的配置信息都移植到程序代码中。下面看一下配置过程。首先,需要在web.xml中配置,把使用 DWR 注释的类都要加在这里。

  清单 16. 对web.xml进行配置
    

以下是引用片段:
 <init-param> 
    <param-name>classes</param-name> 
    <param-value> 
        com.ibm.osl.service.impl.UserManagerImpl, 
        com.ibm.osl.entity.User, 
    </param-value> 
 </init-param>

  接下来对要远程调用的方法进行标注。
  
  清单 17. 配置要远程调用的方法

以下是引用片段:
@RemoteProxy(name=”userManager”,creator=SpringCreator.class, 
 creatorParams={@Param(name=”beanName”,value=”userManager”),}) 
 public class UserManagerImpl implements UserManager { 
    @RemoteMethod 
    public int findExistedUser(String name){ 
 return accountDAO.findExistedUser(String name); 
    } 
    @RemoteMethod 
    public List<User> showUsers(){ 
 return accountDAO.showUsers(); 
    } 
 } 

  @RemoteProxy标注要远程调用的类,也就是告诉DWR,这个class是要暴露出来的。相当于dwr.xml中的<creator>标签。RemoteProxy的name指定暴露出来的DWR接口的名字,creator指定使用哪种创造器,本例中使用SpringCreator。creatorParams指定创造器的其他参数。

  @RemoteMethod标注要远程调用的方法。只有加了@RemoteMethod标注的方法才会被暴露。

  接下来对bean的转换进行配置。

  清单 18. 配置要转换的bean

以下是引用片段:
    
 @DataTransferObject(converter=BeanConverter.class) 
 public class User { 
    private String name; 
    private Integer age; 
    public String getName() { 
        return name; 
    } 
    @RemoteProperty 
    public void setName(String name) { 
        this.name = name; 
    } 
    public Integer getAge() { 
        return age; 
    } 
    public void setAge(Integer age) { 
        this.age = age; 
    } 
 } 

  @DataTransferObject标注在客户端和服务器之间转换类。相当于dwr.xml中的<convert>标签。@RemoteProperty标注在类中需要转换的属性。这里只对name进行标注。进行以上配置后,就可以达到和使用dwr的xml配置文件一样的效果了。

  DWR的安全性

  在使用DWR时要明确哪些类和方法是可以被远程调用的。dwr.xml要求为每一个远程类定义一个create项。还可以通过指定include和exclude元素控制远程调用Bean中可以被调用的方法,而不是把所有的方法都暴露给服务器端。除此之外,如果要 DWR 在转换JavaBean到Javascript时有一定限制,可以控制哪些bean的属性可以被转换。特别地,在做删除和更新操作时,可以通过WebContext得到session来判断用户是否有权限进行该操作。另外,不要在生产环境中打开debug属性。

  结束语

  DWR为在客户端使用服务器端的对象和方法提供了一条捷径,并且DWR还提供了一个Spring的创建器,允许直接调用Spring容器中的bean,将bean转换成一个javascript对象。通过本文,您了解了如何结合使用DWR和Spring MVC。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • 八个超实用的jQuery技巧攻略

    jQuery是JavaScript最好的库之一,主要用于制作动画、事件处理,支持Ajax及HTML脚本客户端。文中分享了8个超实用的jQuery代码技巧攻略,希望你会喜欢。

  • HTML5强大功能背后的安全陷阱

    尽管HTML5使网站的功能更为强大,但开发人员需充分利用其新的技术特征来提高网站的安全性,使用不当会带安全问题,你知道吗?

  • 前端页面开发之Node.js初学者指南

    Node.js是刚刚兴起的一个概念,你对它的了解有多少?Node.js的意义是什么,它是怎么发展起来的?Node.js的作用是怎样的呢?

  • 分析Java语言的复杂数据类型

    除了简单数据类型之外,Java语言中还定义了三种索引数据类型,你知道它们是什么吗?如何初始化过程、默认初值和针对复杂数据类型操作的呢?