Overview
secretepdb
这个项目需要展现一些统计图表,我们使用了google
的gchart
,图表的数据源从数据库异步获取。这里就用到了Struts2
和Jquery Ajax
的集成。本身都是很规范的东西,应该不会出什么问题,但是特定条件下里面有个坑,在这里梳理一遍。
既然是Struts2
跟Ajax
集成,肯定也会用到Struts2
本身的一些东西,可以先看看我之前写的:Struts2总结 这篇文章,接下来可能就好读一些。
与 Struts2总结 中提到的一样,本文使用的依然是struts-2.3.24.1
。
2.1 需要的jar
库
2.1.1 Struts2
接口包
Struts2
内置提供了对各种插件的支持,比如dojo
,json
,sitemesh
等,并在doc
文件夹中中提供了帮助文章,doc/struts2-plugins
这个文件夹中就是所有支持的插件的帮助文档。
我们跟Ajax
通讯所需要的就是就是struts2-json-plugin
这个插件,等等...我们是要集成Ajax
,可为什么要集成struts2-json-plugin
这个看起来毫不相关的插件呢。这是因为,异步传输的通讯方式本身Struts2
就支持,配置下struts.xml
就可以实现,但是JAVA后台跟前端传输需要使用Json
格式传输,而JAVA对象和Json格式的相互转换,就需要用到专有的Json
包,所以Struts2
提供了下面的插件:
- struts2-json-plugin-2.3.24.1.jar
这个插件也可以从struts-2.3.24.1
的官方下载包的struts-2.3.24.1-all.zip
中的lib文件夹中找到。
2.1.2 Json需要的包
处理Json格式需要使用下面的包:
- json-lib-2.4-jdk15.jar
因为Json包是独立的存在,所以jar的版本号跟Struts2
没什么联系。整体来看就是,Struts2
和Json
都是独立存在的,而Struts2
提供了一个插件包,用来将Json
集成到Struts2
中。
json-lib-2.4-jdk15.jar
本身还依赖下面的包:
- commons-beanutils-1.8.0.jar
commons-lang-2.4.jar
- ezmorph-1.0.6.jar
- log4j-1.2.15.jar
- commons-collections-3.2.1.jar
- commons-logging-1.1.14.jar
跟Struts2
所依赖的包情况一样,除了commons-lang-2.4.jar
,其他包版本号都没有限制,最简单的方式就是从struts-2.3.24.1-all.zip
中的lib
文件夹中找,里面都有,实际上因为依赖包跟Struts2
有重叠,只需要添加三个包:commons-beanutils-1.8.0.jar
、commons-collections-3.2.1.jar
和commons-lang-2.4.jar
。
特别需要说明的就是commons-lang-2.4.jar
,因为commons-lang-2.4.jar
和struts-2.3.24.1
依赖的commons-lang3-3.2.jar
包名都不同了,所以同时引入不会产生冲突,而json-lib-2.4-jdk15.jar
依赖的是struts-2.3.24.1
(commons-lang 2.0
版本的包都可以),而不是commons-lang3-3.2.jar
,如果不引入commons-lang-2.4.jar
,不会报错,但是就是没办法成功转化数据格式。
2.2 集成配置
2.2.1 前端js
请求
$.ajax({
type:"post",
url:"ajaxAction/getDataByTypeStatisticsAction",//需要用来处理ajax请求的action,getDataByType为处理的方法名,StatisticsAction为action名,注意这里的ajaxAction/的写法,后面配置struts.xml的时候会提到。
async: true,//通讯是否需要异步执行,默认是true,也就是异步传输,当执行完$.ajax()函数后,js代码继续向下执行,不会等待结果返回。在这里特别的写出来是因为有时候,比如画图,你需要先拿到数据才能画图,因此这里可以设置为false,这样就可以拿到结果,程序才会执行$.ajax()后的代码。
data:{//向后台传递的参数
name:"Chris",
age:5,
position:"tt"//这里不要加"," 不然会报错,而且根本不会提示错误地方
},
dataType:"json",//设置需要返回的数据类型
success:function(data){//回调函数,通讯成功会调用此函数,参数data用于接收后台的返回值。
var d = eval("("+data+")");//将数据转换成json类型,可以把data用alert()输出出来看看到底是什么样的结构
//得到的d是一个形如{"key":"value","key1":"value1"}的数据类型,然后取值出来
alert(""+d.name+"");
alert(""+d.age+"");
alert(""+d.position+"");
},
error:function(){//回调函数,通讯失败会调用此函数
alert("系统异常,请稍后重试!");
}//这里不要加","
});
上面的Js脚本,向名为StatisticsAction
的action
发送异步传输的请求,并传递了三个参数name
、gender
和position
,并指定了StatisticsAction
中的getDataByType
方法处理该请求。具体解释已经注释在代码中了,很容易明白。
2.2.2 配置struts.xml
struts.xml
就是前台和后台的连接,我们已经知道了普通的action
请求应该怎么配,下面给出Struts2
与Ajax
通讯的配置方法:
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.action.excludePattern" value="/static/.*?" />
<!--普通的action配置,extends是struts-default-->
<package name="front" extends="struts-default" >
<action name="searchDBByID" class="action.SearchDBByIDAction" method="execute">
<result>
/searchResults.jsp
</result>
<result name="noResults">
/searchNoResults.html
</result>
</action>
</package>
<!--Ajax通讯需要的action配置,extends必须是json-default-->
<package name="ajax" namespace="/ajaxAction" extends="json-default" >
<action name="*StatisticsAction" method="{1}" class="action.StatisticsAction">
<!--注意,这里的result type要设置成json,里面不需要有返回页面,因为返回值是交给前端的Js接收-->
<result name="success" type="json">
<!-- result是action中设置的变量名,也是页面需要返回的数据,该变量在action中必须有setter和getter方法 -->
<param name="root">result</param>
</result>
</action>
</package>
</struts>
这里面有两个地方需要注意:
- 为了防止混淆,我新建了一个
name
是ajax
的<package>
与原来的并列,并将这个<package>
的namespace
属性设置为了/ajaxAction
,所以前端在请求的时候就不能直接使用action名字了,而是需要使用/ajaxAction/action
名字。 <action name="*StatisticsAction" method="{1}" class="action.StatisticsAction">
中的name
属性是包含通配符的,是为了匹配多action请求,上面Js中请求是url:"ajaxAction/getDataByTypeStatisticsAction"
,其中,ajaxAction/前缀使得这个请求映射到了name
是ajax
的<package>
上,而getDataByTypeStatisticsAction
与action
中的*StatisticsAction
匹配,*
部分就指代了getDataByType
,action
的method="{1}"
相当于method="第一个通配符配到的字符串"
,也就是method="getDataByType"
。
这两个设置,就可以将前台的Js
请求,映射到了指定的action
的指定方法。
2.2.3 StatisticsAction
public class StatisticsAction extends ActionSupport{
//用来接收前端Js的参数,名字与Js中的参数名字必须一致。
private String name;
private int age;
private String position;
//省略了name,age,position的get和set方法,这里只是省略不写,程序中必须要有。
...
//用来存储返回给前端页面的值,需要与struts.xml中配置的名字一致,并且必须有get和set方法。
private String result;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
//Js请求的action方法
public String getDataByType() {
Map<String,Object> map = new HashMap<String,Object>();
//跟传统的action传值一样,这里的name,gender,position由struts接收前端传参后初始化,在action中直接使用就可以了。
map.put("name", name);
map.put("gender",gender);
map.put("position", position)
//将一个JAVA对象(这里是map对象),转化为一个JSON对象。需要import net.sf.json.JSONArray。
JSONObject json = JSONObject.fromObject(map);
//注意,这里的result为String类型,内容为:
//"{name:"Chris",age:5,position:"tt"}"
result = json.toString();
return SUCCESS;
}
}
2.2.4 再看前端js
我们回过头再看一个js中的细节,回调函数:
success:function(data){//回调函数,通讯成功会调用此函数,参数data用于接收后台的返回值。
var d = eval("("+data+")");//将数据转换成json类型,可以把data用alert()输出出来看看到底是什么样的结构
//得到的d是一个形如{"key":"value","key1":"value1"}的数据类型,然后取值出来
alert(""+d.name+"");
alert(""+d.age+"");
alert(""+d.position+"");
}
因为action
返回的是一个String
类型的值,所以这里回调函数接收到的data
,就是一个String
类型的值,但是我们需要的是一个js
的对象类型,因此使用了eval("("+data+")")
将String
类型的值变成了一个js
对象,这是一个通用的做法,所有通过json格式传送到前端的对象,都需要转化为js对象。
可见,使用JSON
是因为js
和JAVA
两种语言对于对象的定义都有自己的标准,不可能将JAVA
中的对象直接传递给js
中使用,因此需要使用JSON
这种统一规范的形式,在JAVA
端,使用了JSON
包提供的JSONObject.fromObject
方法,将一个JAVA
对象转化为JSON
格式的字符串,传递给前端的js
,js
再使用内置的eval()
方法将JSON
格式的字符串转化为js
的对象,从而实现了JAVA
与js
之间的对象传递。
2.3 JSON
简介
直接看阮一峰写的 数据类型和Json格式 吧,对数据类型和JSON
格式解释地简洁又足够好,实在没有理由自己再写一段概念上的介绍。
既然提到阮一峰,不得不推荐一下,黑客与画家
的译者,中国少有的文采好,能把技术层面的东西讲的深入浅出的软件开发者。他让我想到了台湾的侯捷先生,有涵养,文化底蕴好,文字写得有情感,不生硬,非常值得学习。
My name is Ruan YiFeng(阮一峰). You can call me Frank. I was born in 1970s.
I am an IT developer focusing on web technology, and a strong advocate and believer of Free Software. Now I am employed by Alipay.com as an Node/JavaScript engineer.
I have an Economics degree, and once worked for a local college in Shanghai as an assistant professor.
In spare time, I like reading book, surfing internet, watching movie and taking a leisurely walk outdoors.
好像有点跑题了,下面给出几个不错的JAVA
处理JSON
的文章:
入门之后也可以直接查看JSON in Java
的官方API文档: