在struts2中使用JSON

(来源:http://jiangning85.blog.sohu.com/148177571.html)

 

  JSON(Java Script Object Notation),是一种语言无关的数据交换格式。 JSON插件是Structs 2 的Ajax插件,通过利用JSON插件,开发者可以很方便,灵活的利用Ajax进行开发。 Json是一种轻量级的数据交换格式,JSon插件提供了一种名为json的Action ResultType 。 一旦为Action指定了该结果处理类型,JSON插件就会自动将Action里的数据序列化成JSON格式的数据, 并返回给客户端物理视图的JavaScript。简单的说,JSON插件允许我们在JavaScript中异步的调用Action,而且Action不需 要指定视图来显示Action的信息显示。 而是由JSON插件来负责具体将Action里面具体的信息返回给调用页面。 Json的数据格式可简单如下形式: person = { name: 'Jim',age: 18,gender: 'man'}。 如果action的属性很多,我们想要从Action返回到调用页面的数据。 这个时候配置includeProperties或者excludeProperties拦截器即可。
而这2个拦截器的定义都在struts2的json-default包内,所以要使用该拦截器的包都要继承自json-default。
<struts>
    <constant name="struts.objectFactory" value="spring"/>    
    <include file="struts-admin.xml"></include>
    <package name="default" extends="json-default">
        <action name="person" class="com.person.PersonAction" method="view">
        <result type="json">
           <param name="includeProperties">          
            person/.name,persoon/.age,person/.gender          
           </param>>          
        </result>
        </action>
    </package>      
</struts>
利用Struts 2的支持的可配置结果,可以达到过滤器的效果。Action的处理结果配置支持正则表达式。
但是如果返回的对象是一个数组格式的Json数据。比如peson Bean中有对象persion1...person9,而我只要person1的json数据,
则可以用如下的正则表达式。
<struts>
    <constant name="struts.objectFactory" value="spring"/>    
    <include file="struts-admin.xml"></include>
    <package name="default" extends="json-default">
        <action name="person" class="com.person.PersonAction" method="view">
        <result type="json">
           <param name="includeProperties">          
            person/[/d+/]/.person1
           </param>>          
        </result>
        </action>
    </package>      
</struts>
excludeProperties拦截器的用法与此类同,如果拦截的仅仅是一个对象,如果拦截掉person Bean的整个对象,使用如下配置
<struts>
    <constant name="struts.objectFactory" value="spring"/>    
    <include file="struts-admin.xml"></include>
    <package name="default" extends="json-default">
        <action name="person" class="com.person.PersonAction" method="view">
        <result type="json">
           <param name="excludeProperties">          
            person
           </param>>          
        </result>
        </action>
    </package>      
</struts>

需要注意的是,如果用JSON插件把返回结果定为JSON。而JSON的原理是在ACTION中的get方法都会序列化,
所以前面是get的方法只要没指定不序列化,都会执行。
如果该方法一定要命名为get*(比如实现了什么接口),
那么可以在该方法的前面加注解声明该方法不做序列化。
注解的方式为:@JSON(serialize=false)

除此之外,JSON注释还支持如下几个域:
  serialize:设置是否序列化该属性
  deserialize:设置是否反序列化该属性。
  format:设置用于格式化输出、解析日期表单域的格式。例如"yyyy-MM-dd'T'HH:mm:ss"。

//使用注释语法来改变该属性序列化后的属性名
 @JSON(name="newName")
 public String getName()
 {
  return this.name;
 }
需要引入 import com.googlecode.jsonplugin.annotations.JSON;

@JSON(serialize=false)
public User getUser() {
    return this.User;
}

@JSON(format="yyyy-MM-dd")
public Date getStartDate() {
    return this.startDate;
}

    实际在使用的时候,如果不指定includeProperties或者excludeProperties,同时又没有 @JSON(serialize=false),那么会默认返回所有Action中的有get、set方法的属性,通常这样做工程上也没什么问题,而且如 果我们在使用extjs的话,那么当发起一个请求如下所示:

      Ext.Ajax.request({
           url : 'json/ViewShowAction_getChildrenDevices.do?viewName='+viewName,
           disableCaching : true,
           method : 'GET',

           。。。

           })

     然后如果我们想要获取服务器端返回的结果,需要在method后面再添加处理函数,如下所示:

        success : function(result, request) {
    // 获取下级路由器列表并显示
    thisDevice.children = [];
    //以下两句是获得该视图的背景图片url,同时对页面背景进行设置,与该函数的其它功能没有交叉,写在这里不够优雅
    var background=Ext.decode(result.responseText).background;
    var deviceList = Ext.decode(result.responseText).deviceList;
    for (i = 0; i < deviceList.length; i++) {
      thisDevice.children[i] = new cernet2.Device(deviceList[i],thisDevice);
      thisDevice.children[i].showInBigMap();

      。。。
    }
    thisDevice.fetchChildrenDone = true;
    thisDevice.showChildrenLinkInMap(viewName);
   },
   failure : function(result, request) {
    //Ext.log('获取路由器时有错误发生,错误内容为:'+ result.responseText);
   }

   实际上extjs是把所有的Action类里面有get set方法的属性都已JSON格式存入了result.responseText中,我们从那里就可以获得所要的数据,比如var background=Ext.decode(result.responseText).background;一句,是得到服务器端的 background数据(String类型),而var deviceList = Ext.decode(result.responseText).deviceList;则是从服务器端获得了一个deviceList(List类 型)。

 

    “在struts2中使用JSON(一)”中讲述了如何从服务器端得到数据,那么如何才能将请求以JSON的格式传给客户端呢?这个需求可以表述为,如果 客户端要传一个JavaScript对象给服务器端,而且服务器端正好可以有一个与之对应的java对象进行接收。

    实现这个需求的方式有两种:1.使用表单,这里同样以extjs的表单为例:

  var addSwitch = new Ext.FormPanel( {
       labelWidth : 113, // label settings here cascade unless overridden
       frame : true,
       title : '',
       bodyStyle : 'padding:5px 5px 0',
       width : 420,
       defaults : {
           width : 230
       },
       labelAlign: 'left',
       defaultType : 'textfield',
       items : [ {
             fieldLabel : 'Name',
             name : 'switch1.name',
             allowBlank:true
            },{
             id:'ipv4Address',
             fieldLabel : 'IPV4Address',
             name : 'switch1.ipv4Address',
             allowBlank:true
            }, {
             id:'ipv6Address',
             fieldLabel : 'IPV6Address',
             name : 'switch1.ipv6Address',
             allowBlank:true
            }, {
             fieldLabel : 'CommunityName',
             allowBlank:false,
             blankText:"can't be empty!",
             name : 'switch1.communityName'
           },

  。。。。

  对于items中的每一项,我们的name属性都设置成如"switch1.communityName",switch1对应着服务器端action里的switch1属性(它是个对象),switch1对象对应的java类如下:

  public class Switch {
    private Long id;
    private String ipv4Address;
    private String ipv6Address;
    private String name;
    private String communityName;
    private String SNMPVersion;
    private String status;

  。。。

  }

  注意:Switch类对于每个属性都要有get set方法,这样服务器端的switch1对象就可以接收来自前台表单的各个元素。而且此种方法前端是不需要有switch的js类的只需要设置name字段即可。

  这种方法虽然简单,但是局限性比较大,如果我不想传表单的元素,而是直接想把前台的js对象传给后台该怎么做呢?下篇文章会进行详细讲解。

 

    对于我们想把前端的js对象传给后台的java对象接收,可以使用DWR(Direct Web Remoting),是一个开源 的类库,可以帮助开发人员开发包含AJAX 技术的网站.它可以允许在浏览器里的代码使用运行在WEB服务器上的JAVA函数,就像它就在浏览器里一样.作为一个java open source library,DWR 可以帮助开发人员完成应用AJAX技术的web程序。它可以让浏览器上的javascript方法调用运行在web服务器上java方法。 DWR 主要由两部门组成.javascript与web服务器通信并更新web页;运行在web服务器的Servlet处理请求并把响应发回浏览器。 DWR 采 用新颖的方法实现了AJAX(本来也没有确切的定义),在java代码基础上动态的生成javascript代码。web开发者可以直接调用这些 javascript代码,然而真正的代码是运行在web服务器上的java code。出与安全考虑,开发者必须配置哪些java class暴露给DWR .(dwr .xml) 。这种从(java到javascript)调用机制给用户一种感觉,好象常规的RPC机制,或RMI or SOAP.但是它运行在web上,不需要任何浏览器插件。 DWR 不认为浏览器和web服务器之间协议重要,把系统界面放在首位。最大挑战是java method call的同步特征与ajax异步特性之间的矛盾。在异步模型里,结果只有在方法结束后才有效。DWR 解决了这个问题,把回调函数当成参数传给方法,处理完成后,自动调用回调方法。

 一、使用方法

1.1、dwr.xml的配置

 

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
    "http://getahead.org/dwr/dwr20.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="InterfaceAction" scope="script">
      <param name="class" value="com.config.action.InterfaceAction"/>
      <include method="changeInterfaceInfo"/>
    </create>
    <convert match="com.config.dto.IfInterface" converter="bean"/>
    </allow>
</dwr>

 

    <allow>标签中包括可以暴露给javascript访问的东西。<create>标签中指定javascript中可以访 问的java类,并定义DWR应当如何获得要进行远程的类的实例。creator="new"属性指定java类实例的生成方式,new意味着DWR应当 调用类的默认构造函数来获得实例,其他的还有spring方式,通过与IOC容器Spring进行集成来获得实例等等。 javascript="InterfaceAction"属性指定javascript代码访问对象时使用的名称。<param name="class" value="com.config.action.InterfaceAction"/>指定相关联的java类,<include method="changeInterfaceInfo"/>表明InterfaceAction的changeInterfaceInfo方法 可以暴露给js使用。<convert match="com.config.dto.IfInterface" converter="bean"/>这一项非常关键,表明了前端生成一个IfInterface的JS对象,可以直接传给后端,并将 IfInterface的java对象赋值。注意:IfInterface必须每个属性的有get set方法。地方

1.2、web.xml的配置

在web.xml中加上如下配置项:

  <servlet-name>dwr-invoker</servlet-name>

   <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
   <init-param>
      <param-name>debug</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>

  表明所有的/dwr/*请求都交由此servlet处理。

  1.3、javascript中调用

  首先,引入javascript脚本

 <script type='text/javascript' src='dwr/interface/SwitchAction.js'></script> 
 <script type='text/javascript' src='dwr/engine.js'></script>
 <script type='text/javascript' src='dwr/util.js'></script>(可选)
 其中SwitchAction.js是dwr根据配置文件自动生成的,engine.js和util.js是dwr自带的脚本文件。

 其次,编写调用java方法的javascript函数
InterfaceAction.changeInterfaceInfo(interface,ipv4Address,ipv6Address, {          callback:handleChangeInterfaceInfo,
timeout:5000,
errorHandler:function(message) { alert("Oops: " + message);}
});

其中handleChangeInterfaceInfo是接收服务器端结果的回调函数,如下所示:

function handleChangeInterfaceInfo(result) {
  if(result=='success'){
   store.reload();
  }else{
   Ext.MessageBox.alert('',"Set operation has failed!");
   store.reload();
  }
}

其中的参数interface(js类)定义如下:

savi.Interface = function() {
 this.ifIndex;
 this.validationStatus;
 this.trustStatus;
 this.bindingNum;
 this.ipVersion;
}

然后通过 var interface=new savi.Interface();并对其属性赋值。

 

而对应的java端方法如下:

public String changeInterfaceInfo(IfInterface ifInterface,String ipv4Address,String ipv6Address){

}

IfInterface是于js中的Interface对应的java类,定义如下:

public class IfInterface {
 private Integer ifIndex;
 private Integer ipVersion;
 private Integer validationStatus;
 private Integer trustStatus;
 private Integer bindingNum;

。。。

}

注意:所有属性要有get set方法。

二、源码解析

dwr 的设计很象webwork2的设计,隐藏http协议,扩展性,兼容性及强。

通过研究uk.ltd.getahead.dwr .DWRServlet这个servlet来研究下dwr 到底是如何工作的。这样所有的/dwr /*所有请求都由这个servlet来处理,它到底处理了些什么?

1 、  web服务器启动,DWRServlet init()方法调用,init主要做了以下工作。   

设置日志级别、实例化DWR 用到的单例类(这些类在jvm中只有一个实例对象)、读去配置文件(包括dwr .jar包中的dwr .xml,WEB-INF/dwr .xml. config*.xml)。   

2 、请求处理   

DWRServlet.doGet, doPost方法都调用processor.handle(req, resp)方法处理。Processor对象在init()方法中已经初始化了。   

public   void  handle(HttpServletRequest req, HttpServletResponse resp)  throws  IOException  {   

        String pathinfo = req.getPathInfo();;   

         if (pathinfo ==  null  || pathinfo.length(); ==  0  || pathinfo.equals( "/" ););   

        {   

            resp.sendRedirect(req.getContextPath(); + req.getServletPath (); +  '/'  +  "index.html" );;   

        }  else   

         if (pathinfo !=  null  && pathinfo.equalsIgnoreCase( "/index.html" ););   

        {   

            doIndex(req, resp);;   

        }  else   

         if (pathinfo !=  null  && pathinfo.startsWith( "/test/" ););   

        {   

            doTest(req, resp);;   

        }  else   

         if (pathinfo !=  null  && pathinfo.equalsIgnoreCase( "/engine.js" ););   

        {   

            doFile(resp,  "engine.js" ,  "text/javascript" );;   

        }  else   

         if (pathinfo !=  null  && pathinfo.equalsIgnoreCase( "/util.js" ););   

        {   

            doFile(resp,  "util.js" ,  "text/javascript" );;   

        }  else   

         if (pathinfo !=  null  && pathinfo.equalsIgnoreCase( "/deprecated.js" ););   

        {   

            doFile(resp,  "deprecated.js" ,  "text/javascript" );;   

        }  else   

         if (pathinfo !=  null  && pathinfo.startsWith( "/interface/" ););   

        {   

            doInterface(req, resp);;   

        }  else   

         if (pathinfo !=  null  && pathinfo.startsWith( "/exec" ););   

        {   

            doExec(req, resp);;   

        }  else   

        {   

            log.warn( "Page not found. In debug/test mode try viewing /[WEB-APP]/dwr /" );;   

            resp.sendError( 404 );;   

        }   

    }   

哦。这些恍然大悟。dwr /*处理的请求也就这几种。   

( 1 )dwr /index.html,dwr /test/这种只能在debug模式下使用,调试用。   

dwr /engine.js,dwr /util.js,dwr /deprecated.js当这个请求到达,从dwr .jar包中读取文件流,响应回去。(重复请求有缓存)   

( 2 )当dwr / interface /这种请求到来,(例如我们在index.html中的 <script type= 'text/javascript'  src= 'dwr /interface/JDate.js' ></script>)DWR 做一件伟大的事。把我们在WEB-INF/dwr .xml中的   

<create creator= "new"  javascript= "JDate" >   

 <param name= "class"  value= "java.util.Date" />   

</create>   

Java.util.Date转化为javascript函数。   

http: //localhost:port/simpledwr/dwr /interface/JDate.js看看吧。   

细节也比较简单,通过java反射,把方法都写成javascript特定的方法。(我觉得这些转换可以放到缓存里,下次调用没必要再生成一遍,不知道作者为什么没这样做)。   

( 3 )dwr /exec   

   javascript调用方法时发送这种请求,可能是XMLHttpRequest或IFrame发送。   

当然,javascript调用的方法签名与java代码一致,包括参数,还有javascript的回调方法也传到了服务器端,在服 务器端很容易实现。回调方法的java的执行结果 返回类似 <script>callMethod(结果)<script>的 javascript字符串,在浏览器执行。哈,一切就这么简单,巧妙。       

dwr 的设计构思很是巧妙。   

第一、把java类转化为javascript类由dwr 自动完成,只需简单的配置。   

第二、应用起来极其简单。开发者不要该服务器代码就可以集成。   

第三、容易测试。和webwork一样,隐藏的http协议。   

第四、及强扩展性。例如与spring集成,只需修改一点代码。   

第五、性能。就我与jason,等简单比较,dwr 性能可能是最好的。   

第六、自动把java对象转化为javascript对象,并且及易扩展。  

二、参考网址

1.http://www.javaeye.com/topic/16424

2.http://baike.baidu.com/view/73492.htm

3.http://directwebremoting.org/dwr/introduction/scripting-dwr.html

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值