今天我要将json和ajax引入到我所写的框架,不过今天用到的技术有部分不是我框架最终使用到的技术,比如ajax技术,我用到的是最为原始的ajax技术,这次算是对老技术的回顾,不过不管技术如何演进,对技术的本质的掌握都是十分重要的。
首先我简单介绍下json的基础知识。
json的定义是:基于JavaScript语言的轻量级的数据交换格式(JavaScript Object Notiation)。(摘录于有道)
JSON建构于两种结构:
1. “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),记录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
2. 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
在我写到的架构里,前端和服务端的交互统一通过json来进行,这个做法符合我的观点,所有逻辑层的通讯都是通过键值对的方式进行,,参见我以前的博文:《系统设计与架构笔记:键值对在架构设计里的应用》
下面我们继续写框架,首先我新建一张表,它的结构如下图:
下面我将为这张表建立dao、service以及action,下面我将代码贴在下面(按我开发的过程):
首先看看工程新的结构目录,如下图:
1.首先是/ssiprj/src/cn/com/sharpxiajun/dao/sqlmap包下的映射文件PRODUCT.xml,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="PRODUCT">
<select id="queryProductList" parameterClass="java.util.Map" resultClass="java.util.HashMap">
select t.id,t.name,t.desc,t.create_date,t.modify_date,t.status from product t
<dynamic prepend="where">
<isNotEmpty prepend="and" property="name">
t.name = #name:VARCHAR#
</isNotEmpty>
</dynamic>
</select>
</sqlMap>
2.在/ssiprj/conf包的SqlMapConfig.xml文件里添加PRODUCT.xml路径,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" errorTracingEnabled="true"
maxRequests="64" maxSessions="20" maxTransactions="10"
useStatementNamespaces="true"/>
<sqlMap resource="cn/com/sharpxiajun/dao/sqlmap/USERS.xml"/>
<sqlMap resource="cn/com/sharpxiajun/dao/sqlmap/PRODUCT.xml"/>
</sqlMapConfig>
3.在cn.com.sharpxiajun.dao包下建立接口ProductDao,代码如下:
package cn.com.sharpxiajun.dao;
import java.util.List;
import java.util.Map;
public interface ProductDao {
public static final String QUERY_PRODUCT_LIST_SQL = "PRODUCT.queryProductList";
public List<Map<String, Object>> queryProductList(Map<String, Object> params) throws Exception;
}
4.在cn.com.sharpxiajun.dao.impl包下实现接口ProductDao的类ProductDaoImpl,代码如下:
package cn.com.sharpxiajun.dao.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.stereotype.Repository;
import cn.com.sharpxiajun.dao.ProductDao;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Repository("productDao")
public class ProductDaoImpl implements ProductDao {
@Autowired
@Qualifier("sqlMapClientTemplate")
private SqlMapClientTemplate sqlMapClientTemplate = null;
@Override
public List<Map<String, Object>> queryProductList(Map<String, Object> params)
throws Exception {
return this.sqlMapClientTemplate.queryForList(QUERY_PRODUCT_LIST_SQL, params);
}
}
5.在cn.com.sharpxiajun.service包下建立接口ProductService,代码如下:
package cn.com.sharpxiajun.service;
import java.util.List;
import java.util.Map;
public interface ProductService {
public List<Map<String, Object>> queryProductList(Map<String, Object> params) throws Exception;
}
6.在cn.com.sharpxiajun.service.impl包下建立ProductServiceImpl,他实现ProductService接口,代码如下:
package cn.com.sharpxiajun.service.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.com.sharpxiajun.dao.ProductDao;
import cn.com.sharpxiajun.service.ProductService;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Transactional
@Service("productService")
public class ProductServiceImpl implements ProductService {
@Autowired
@Qualifier("productDao")
private ProductDao productDao = null;
@Override
public List<Map<String, Object>> queryProductList(Map<String, Object> params)
throws Exception {
return this.productDao.queryProductList(params);
}
}
7.在cn.com.sharpxiajun.action包下建立ProductAction,代码如下:
package cn.com.sharpxiajun.action;
import java.sql.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import cn.com.sharpxiajun.common.action.BaseAction;
import cn.com.sharpxiajun.service.ProductService;
@SuppressWarnings("serial")
@Scope("prototype")
@Controller("productAction")
public class ProductAction extends BaseAction {
@Autowired
@Qualifier("productService")
private ProductService productService = null;
//输入值
private String namequery = null;
private Integer id = 0;
private String name = null;
private String desc = null;
private Date createDate = null;
private Date modifyDate = null;
private Boolean status = null;
//输出值
private List<Map<String, Object>> results = null;//返回查询的列表
private String msg = null;//系统运行信息
private String flag = null;//操作状态标记
private String welcome = null;//欢迎语
public String productinit() throws Exception
{
welcome = "欢迎使用本系统!";
return SUCCESS;
}
public String queryProductList() throws Exception
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", namequery);
results = this.productService.queryProductList(map);
flag = "success";
msg = "查询操作成功!";
welcome = "你的查询操作已经完成!";
return SUCCESS;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public List<Map<String, Object>> getResults() {
return results;
}
public void setResults(List<Map<String, Object>> results) {
this.results = results;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getWelcome() {
return welcome;
}
public void setWelcome(String welcome) {
this.welcome = welcome;
}
public String getNamequery() {
return namequery;
}
public void setNamequery(String namequery) {
this.namequery = namequery;
}
}
8.在WEB-INF\jsp包下,建立product.jsp文件,代码如下:
要在struts2程序里使用json,首先http://code.google.com/p/jsonplugin/downloads/list下载JSON插件的JAR包,我下载的版本是jsonplugin-0.34.jar,把jar包加入到工程的lib里,修改conf包下的struts-action.xml文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="action" extends ="struts-default">
<action name="queryUsersList" class="usersAction" method="queryUsersList">
<result name="success">/WEB-INF/jsp/users.jsp</result>
</action>
<action name="usersinit" class="usersAction" method="usersinit">
<result name="success">/WEB-INF/jsp/users.jsp</result>
</action>
<action name="addUsers" class="usersAction" method="addUsers">
<result name="success">/WEB-INF/jsp/users.jsp</result>
<result name="error">/WEB-INF/common/error.jsp</result>
</action>
</package>
<package name="json" extends ="json-default">
<action name="productinit" class="productAction" method="productinit">
<result name="success">/WEB-INF/jsp/product.jsp</result>
</action>
<action name="queryProductList" class="productAction" method="queryProductList">
<result name="success" type="json"></result>
</action>
</package>
</struts>
上面配置文件的“package”元素和以往不同的是,它扩展了“json-default”而不是“struts-default”。“json-default”是在jsonplugin-0.34.jar包里的struts-plugin.xml中定义的。该文件同时定义了“json”的结果类型,有兴趣的朋友可以打开此文件看看。
下面我们启动tomcat服务器,在浏览器地址栏里填入下面地址:http://localhost:8080/ssiprj/productinit.action
页面如下图:
点击按钮query,结果是:
选择用记事本打开,显示结果如下:
{"createDate":null,"desc":null,"flag":"success","id":0,"modifyDate":null,"msg":"查询操作成功!","name":null,"namequery":"","results":
[{"id":1,"desc":"apple","create_date":"2011-10-28T00:00:00","status":true,"modify_date":"2011-10-28T00:00:00","name":"ipad"},
{"id":2,"desc":"google","create_date":"2011-10-28T00:00:00","status":true,"modify_date":"2011-10-28T00:00:00","name":"android"}],"status":null,"welcome":"你的查询操作
已经完成!"}
结果居然是一个下载的文本文件,这个不符合我们的要求,下面我用ajax技术来处理这些返回结果。
AJAX技术:服务端返回客户端JSON数据
上面的例子里,我们服务端程序返回到前端页面的数据格式是json方式,下面的程序就是使用ajax来访问服务端程序并且展示页面如何处理json对象,这里我们只需要修改product.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>JSON</title>
</head>
<body>
名称:<input type="text" id="namequery" name="namequery"/> <input type="button" onClick="queryBtnClick();" value="query"/>
<div id="datashow"></div>
</body>
</html>
<script src="<%= request.getContextPath()%>/js/json.js"/></script>
<script type="text/javascript">
var xmlHttp;//XMLHttpRequest对象
//创建XMLHttpRequest对象
function createXMLHttpRequest()
{
if (window.ActiveXobject)
{
xmlHttp =new ActiveXOject('Microsoft.XMLHTTP');
}elseif (window.XMLHttpRequest)
{
xmlHttp =new XMLHttpRequest();
}
}
//查询操作
function queryBtnClick()
{
var url ="queryProductList.action";
createXMLHttpRequest();
xmlHttp.open("POST",url,true);
xmlHttp.onreadystatechange = jsonCallBack;
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");//使用POST传递信息时候用到的
xmlHttp.send(null);
}
//处理返回数据的回调函数
function jsonCallBack()
{
if (xmlHttp.readyState ==4)
{
if (xmlHttp.status ==200)
{
parseResults();
}
}
}
//处理返回数据的方法
function parseResults()
{
var product = eval('('+ xmlHttp.responseText +')');
var showstr ="<table border='1'>";
for (var i =0;i < product.results.length;i++)
{
obj = product.results[i];
showstr +='<tr>';
showstr +='<td>'+ obj.id +'</td>';
showstr +='<td>'+ obj.name +'</td>';
showstr +='<td>'+ obj.desc +'</td>';
showstr +='<td>'+ obj.create_date +'</td>';
showstr +='<td>'+ obj.modify_date +'</td>';
showstr +='<td>'+ obj.status +'</td>';
showstr +='</tr>';
}
showstr +='</table>';
document.getElementById('datashow').innerHTML = showstr;
}
</script>
这次提交服务端请求里没有放入任何参数,也就是说前端没有传参数到服务端,因此这个测试代码就是展示ajax是如何调用的以及如何处理返回数据是json格式的数据,我们点击页面的query按钮,结果如下:
大家看到了数据成功返回并且被正确处理。
AJAX技术:客户端传给服务端JSON数据
大家在http://www.json.org/网站里下载json.js脚本以及json在java里的操作类,我下载了这些文件,把他们放到我写的工程里,结果如下:
(我发现我的jsp文件夹放的位置不对了,导致最后在struts-action.xml里面放回页面前还要加/WEB-INF/,哎,算了,将错就错吧!)
我们修改ProductAction.java文件,代码如下:
package cn.com.sharpxiajun.action;
import java.sql.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import cn.com.sharpxiajun.common.action.BaseAction;
import cn.com.sharpxiajun.common.util.JSONObject;
import cn.com.sharpxiajun.service.ProductService;
@SuppressWarnings("serial")
@Scope("prototype")
@Controller("productAction")
public class ProductAction extends BaseAction {
@Autowired
@Qualifier("productService")
private ProductService productService = null;
//输入值
private String namequery = null;
private Integer id = 0;
private String name = null;
private String desc = null;
private Date createDate = null;
private Date modifyDate = null;
private Boolean status = null;
private String jsonQuery = null;//获得json格式的参数
//输出值
private List<Map<String, Object>> results = null;//返回查询的列表
private String msg = null;//系统运行信息
private String flag = null;//操作状态标记
private String welcome = null;//欢迎语
public String productinit() throws Exception
{
welcome = "欢迎使用本系统!";
return SUCCESS;
}
public String queryProductList() throws Exception
{
Map<String, Object> map = new HashMap<String, Object>();
//map.put("name", namequery);
JSONObject jsonObject = new JSONObject(jsonQuery);//把查询参数转化为json对象
map.put("name", jsonObject.get("namequery"));
results = this.productService.queryProductList(map);
flag = "success";
msg = "查询操作成功!";
welcome = "你的查询操作已经完成!";
return SUCCESS;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public List<Map<String, Object>> getResults() {
return results;
}
public void setResults(List<Map<String, Object>> results) {
this.results = results;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getWelcome() {
return welcome;
}
public void setWelcome(String welcome) {
this.welcome = welcome;
}
public String getNamequery() {
return namequery;
}
public void setNamequery(String namequery) {
this.namequery = namequery;
}
public String getJsonQuery() {
return jsonQuery;
}
public void setJsonQuery(String jsonQuery) {
this.jsonQuery = jsonQuery;
}
}
修改页面product.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>JSON</title>
</head>
<body>
名称:<input type="text" id="namequery" name="namequery"/> <input type="button" onClick="queryBtnClick();" value="query"/>
<div id="datashow"></div>
</body>
</html>
<script src="<%= request.getContextPath()%>/js/json.js"/></script>
<script type="text/javascript">
var xmlHttp;//XMLHttpRequest对象
//创建XMLHttpRequest对象
function createXMLHttpRequest()
{
if (window.ActiveXobject)
{
xmlHttp =new ActiveXOject('Microsoft.XMLHTTP');
}elseif (window.XMLHttpRequest)
{
xmlHttp =new XMLHttpRequest();
}
}
//查询操作
function queryBtnClick()
{
var url ="queryProductList.action";
createXMLHttpRequest();
xmlHttp.open("POST",url,true);
xmlHttp.onreadystatechange = jsonCallBack;
var obj = getQueryParams();//获取查询参数对象
var queryJson ="jsonQuery="+ JSON.stringify(obj);//构建查询参数
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");//使用POST传递信息时候用到的
xmlHttp.send(queryJson);//将参数发送到服务端
}
//处理返回数据的回调函数
function jsonCallBack()
{
if (xmlHttp.readyState ==4)
{
if (xmlHttp.status ==200)
{
parseResults();
}
}
}
//处理返回数据的方法
function parseResults()
{
var product = eval('('+ xmlHttp.responseText +')');
var showstr ="<table border='1'>";
for (var i =0;i < product.results.length;i++)
{
obj = product.results[i];
showstr +='<tr>';
showstr +='<td>'+ obj.id +'</td>';
showstr +='<td>'+ obj.name +'</td>';
showstr +='<td>'+ obj.desc +'</td>';
showstr +='<td>'+ obj.create_date +'</td>';
showstr +='<td>'+ obj.modify_date +'</td>';
showstr +='<td>'+ obj.status +'</td>';
showstr +='</tr>';
}
showstr +='</table>';
document.getElementById('datashow').innerHTML = showstr;
}
//获取查询参数对象
function getQueryParams()
{
returnnew queryObj();
}
//构建查询参数类
function queryObj()
{
this.namequery = document.getElementById('namequery').value;
}
</script>
代码里面我写了注释,这里就不做过多解释了。
我们在浏览器地址栏里录入:http://localhost:8080/ssiprj/productinit.action,在名称框里录入ipad,点击query查询按钮,结果如下:
控制台打印日志:
|statement| select t.id,t.name,t.desc,t.create_date,t.modify_date,t.status from product t where t.name = 'ipad'
|resultset|
|commit|
返回结果是:
[{id=1, desc=apple, create_date=2011-10-28, status=true, modify_date=2011-10-28, name=ipad}]
拦截器执行结束!!
操作成功了!!!!
最后总结下了:struts2把返回给客户端的数据封装成json格式,前端再处理,这个做起来很自然,也带来了编写代码的便利,但是从客户端到服务端这种做法就有点奇怪了,至少没有看到便利之处,当然今天写的代码都很简单,没有把许多功能抽象出来,不管怎样,让action把返回值封装成json,前端的参数也封装成json格式这就是我的框架里面所追求的。下一篇文章我将引入jquery了,为了展示数据我会使用到jqgrid,下一个阶段我将着重写写前端技术。