2014
08-31

AJAX

Framework of ajax

1、概述

AJAX(Asynchronous JavaScript And XML),异步 JavaScript 及 XML,是基于JavaScript、XML、HTML等技术的新用法。老技术新用法。

2、同步和异步

1)传统 web交互方式——同步
浏览器必须要等到服务器端响应后才能继续下一步操作,中间只能等待。全局刷新。

15. AJAX171

2)AJAX支持的web交互方式——异步

用户通过浏览器的UI层和其中的AJAX引擎交互,发出请求后可以继续做自己的事,无需等待。Ajax引擎和服务器交互。局部刷新。

15. AJAX257

异步,对用户更友好。

3、AJAX编码步骤

1)创建xmlHttp请求对象

XMLHttp请求是AJAX的基础,用于在后台与服务器交互。这意味着可以在不重新加载整个网页的情况下,对网页的局部进行更新。

所有现代浏览器均支持 XMLHttpRequest 对象,创建方式如下:

//由于很多地方需要创建XMLHttpRequest对象,因此将它写到一个函数中
function getXmlHttpRequest(){
	var xhr;
	if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
	  xhr=new XMLHttpRequest();
	}else{// code for IE6, IE5
	  xhr=new ActiveXObject("Microsoft.XMLHTTP");
	}
	return xhr;
}

2)注册回调函数,监控xmlHttp请求状态(readyState)

3)建立与服务器的异步连接(open

4)发出异步请求(send

下面的代码是AJAX编程的框架,分别对应着上面的4个步骤:

var xhr = getXmlHttpRequest();

xhr.onreadystatechange=function(){//注册回调函数,xmlhttp请求状态变化时调用
	if(xhr.readyState==4){//表示响应结束
		if(xhr.status==200){//响应正常
			alert("服务器端已接受您的请求");
		}
	}
};

xhr.open("GET","/ajax/servlet/ServletDemo1?time="+new Date().getTime());

xhr.send(null);

4、XMLHttpRequest对象详解

1)属性

readyState:标识xmlhttp的请求状态,只读

readyState in AJAX

② status 响应状态码 | statusText 响应状态码描述

如响应状态码为200时,描述为OK,表示响应正确;响应状态码为404时,描述为Not Found,表示请求资源不存在。
注:响应完毕(readyState=4)和响应正确(status=200)不一样!

responseText:String类型。将响应正文当做纯文本看待,返回字符串。响应字段”Content-Type”设置为”text/*”即可。后面的案例中会有用到。

④ responseXML:Document对象。将响应正文看成一个XML文档,并返回代表该文档的Document对象。要求响应字段”Content-Type”必须设置为text/xml。后面的案例中会有用到。

2)方法

open(var method,var url [,boolean isAsyn]):建立连接

method:请求方式,url:请求资源URL,isAsyn表示建立的连接是否为异步的,默认为true,所以通常不指定该参数。

send(var data):向服务器发送请求正文。

  • get方式没有请求正文,传入null:send(null);
  • post方式:send(“username=abc&password=123″);

setRequestHeader(var headerName,var headerValue):设置请求头

getResponseHeader(var headerName):获取响应头

3)xmlhttp请求对象的状态监听器:onreadystatechange

当xmlhttp请求的readyState状态发生变化时,触发onreadystatechange事件,调用相应回调函数。一般写法(比较固定):

xhr.onreadystatechange=function(){//注册事件的回调函数
if(xhr.readyState==4){
if(xhr.status==200){
//JS之DOM、BOM编程
}
}
}

5、AJAX案例

一般需要在回调函数中对服务器返回的数据进行处理。服务器返回数据格式常见的有:普通文本、HTML片段、JSON格式的数据、XML格式的数据。下面用例子分别进行讲解。

1)服务器响应输出普通文本

案例描述:在填写注册信息时,输入用户名,如果已存在,则在input框右边提示已存在,且不能刷新当前页面。

①注册页面:

------------------------------------  HTML部分  ------------------------------------
用户名:<input type="text" name="name" id="name" /><span id="msg"></span>
------------------------------------  JS部分  ------------------------------------
window.onload = function(){
	document.getElementById("name").onblur=function(){//失去焦点时
		var xhr = getXmlHttpRequest();
		xhr.onreadystatechange=function(){
			if(xhr.readyState==4){//响应完毕
				if(xhr.status==200){//响应正确
					//改写页面DOM模型
			document.getElementById("msg").innerHTML=xhr.responseText;
				}
			}
		};
		//① get请求方式
	/* 	xhr.open("GET","/AJAX/servlet/CheckNameServlet?time="+new Date().getTime()+"&name="+this.value);
		xhr.send(null); */

		//② POST请求方式
		xhr.open("POST","/AJAX/servlet/CheckNameServlet?time="+new Date().getTime());
		//由于input标签没有用form套起来,所以必须指明请求正文的MIME类型,服务器端才能获取到请求参数。				xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		xhr.send("&name="+this.value);
	};
};

② CheckNameServlet

//用数组模拟了一下用户名库
private static String[] names = {"abc","bcd","cde"};

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setContentType("text/plain;charset=utf-8");
	PrintWriter out = response.getWriter();

	String name  = request.getParameter("name");

	if(Arrays.asList(names).contains(name)){
		out.write("<font color='red'>用户名已存在</font>");
	}else{
		out.write("√");
	}
}

2)服务器响应输出一个页面

案例描述:当客户端访问showProductInfos.html,点击查看按钮发出异步请求时,服务器组织商品列表页面(productList.jsp)输出给客户端。

① showProductInfos.html:客户端

------------------------------------  HTML部分  ------------------------------------
<input type="button" value="查看所有商品信息" id="bt1"/>
<hr/>
<div id ="info"></div>
------------------------------  JS中处理服务器返回结果部分  ------------------------------
document.getElementById("info").innerHTML = xhr.responseText;

② productList.jsp如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<table border=1 cellspacing="10px">
	<tr>
		<th>编号</th>
		<th>商品名</th>
		<th>价格</th>
	</tr>
	<c:forEach items="${products }" var="p" varStatus="vs">
		<tr>
			<td>${p.id }</td>
			<td>${p.name }</td>
			<td>${p.price }</td>
		</tr>
	</c:forEach>
</table>

③ ProductInfoServlet:后台处理页面

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setContentType("text/html;charset=utf-8");
	//用ProductDB模拟了商品库
	List<Product> products = ProductDB.getAllProducts();

	request.setAttribute("products", products);
	//请求转发,将productList.jsp返回给用户
	request.getRequestDispatcher("/productList.jsp").forward(request,response);
}

3)服务器响应输出JSON文本

在上面的例子中,服务器端直接将一个JSP生成的HTML页面(包括数据、HTML等)发送给用户,一旦该HTML页面很大时,必然会影响客户端体验。

如果只将页面中的数据传给客户端,再由客户端对数据进行组织、显示,则可以减少客户端等待时间,提升用户体验。

解决方法:在服务器端将数据组织成JSON文本输出给客户端。

如果手工将List products组织成一个JSON文本,会很麻烦,此时可借助json-lib工具。json-lib还需要依赖:commons-lang、commons-beanutils、commons-collections、commons-logging、ezmorph等。关于JSON-lib的使用案例见本文后面的附录部分(很简单)。

① ProductInfoJsonServlet:将数据组织成JSON文本返回给客户端

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setContentType("text/html;charset=utf-8");
	PrintWriter out = response.getWriter();
	// 用ProductDB模拟了商品库
	List<Product> products = ProductDB.getAllProducts();
	JSONArray ja = JSONArray.fromObject(products);
	/* 生成的JSON文本为:
	 * [{"id":1,"name":"感冒药","price":18},
	 * {"id":2,"name":"救生圈","price":98},
	 * {"id":3,"name":"苹果手机","price":5118}]
	 */
	out.write(ja.toString());
}

② showProductInfos.html:客户端

------------------------------------  HTMl部分  ------------------------------------
<input type="button" value="查看所有商品信息" id="bt1"/>
<hr/>
<div id ="info"></div>
------------------------------  JS中处理服务器返回结果部分  ------------------------------
//JSON字符串 --> JS对象(这儿是数组)
var products= eval("("+xhr.responseText+")");
//在客户端将数据组织成页面显示给用户
var infoHtml = "";
for(var i=0;i < products.length;i ++){
	infoHtml += products[i].id +":"+ products[i].name +"、"+ products[i].price + "<br />";
}
document.getElementById("info").innerHTML = infoHtml;

4)服务器响应输出XML文本

背景同上面。在服务器端将数据组织成XML文本,在客户端通过DOM操作组织成响应的页面展示给用户。用着没有JSON方便,主要是因为XML的解析没有JSON方便。

在服务器端将JAVA对象转换成XML文本可借助于XStream。XStream还依赖于xpp3(xml pull解析)。XStream的使用见附录部分。

案例描述:省、市二级联动的案例中,省、市数据由服务器端用XML的格式传给客户端,然后在客户端组织成页面部分。

① ProvinceCityServlet:将省市数据组织成XML的格式。

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setContentType("text/xml;charset=utf-8");
	PrintWriter out = response.getWriter();

	//ProvinceDB模拟了省、市数据库(省->市:一对多)
	List<Province> provinces = ProvinceDB.getAllProvince();

	XStream xstream = new XStream();
	xstream.autodetectAnnotations(true);//使用了XStream注解需加上这句话

	//设置别名:List对应的默认元素名为java.util.List
	xstream.alias("provinces", List.class);
	//provinces-->xml文本,并响应输出给客户端
	out.write(xstream.toXML(provinces));
}

② 客户端解析部分从略(比较麻烦,一般用JSON传数据就够了)。

6、附录

1)JSON-lib的用法

核心类和方法:

  • JSONArray【 fromObject(obj [,JSONConfig] ) 、toString() 】
  • JSONObject【 fromObject(obj [,JSONConfig] ) 、toString() 】
  • JSONConfig【 setExcludes(String[] ) 】

JSON-lib的用法如下(在JUnit中测试):

public class JSONLibTest {
	// 数组、List、Set-->JSON字符串,都是一样的
	@Test
	public void test1() {
		String[] str = { "aaa", "bbb", "ccc" };
		JSONArray ja = JSONArray.fromObject(str);
		// 输出["aaa","bbb","ccc"]
		System.out.println(ja);
	}

	// Map-->JSON字符串,数组格式,不建议
	@Test
	public void test2() {
		Map<String, String> map = new HashMap<String, String>();
		map.put("a", "aaa");
		map.put("b", "bbb");
		JSONArray ja = JSONArray.fromObject(map);
		// 输出[{"b":"bbb","a":"aaa"}]
		System.out.println(ja);
	}

	// Map-->JSON字符串,JSON对象格式,建议
	@Test
	public void test3() {
		Map<String, String> map = new HashMap<String, String>();
		map.put("a", "aaa");
		map.put("b", "bbb");
		JSONObject ja = JSONObject.fromObject(map);
		// 输出{"b":"bbb","a":"aaa"}
		System.out.println(ja);
	}

	// javaBean-->JSON字符串,JSON对象格式,建议
	@Test
	public void test4() {
		User user = new User("xx", "xx123");
		JSONObject jo = JSONObject.fromObject(user);
		// 输出{"password":"yy123","username":"yy"}
		System.out.println(jo);
	}

	// List<JavaBean>-->JSON字符串,JSON对象数组格式,建议
	@Test
	public void test5() {
		List<User> users = new ArrayList<User>();
		users.add(new User("xx", "xx123"));
		users.add(new User("yy", "yy123"));
		// 输出[{"password":"xx123","username":"xx"},{"password":"yy123","username":"yy"}]
		JSONArray ja = JSONArray.fromObject(users);
		System.out.println(ja);
	}

	// List<JavaBean>-->JSON字符串,JSON对象数组格式
	// 可以去掉不需要显示的属性
	@Test
	public void test6() {
		List<User> users = new ArrayList<User>();
		users.add(new User("xx", "xx123"));
		users.add(new User("yy", "yy123"));

		//JSON配置,去掉不需要显示的属性
		JsonConfig jc = new JsonConfig();
		jc.setExcludes(new String[] {"password"});

		JSONArray ja = JSONArray.fromObject(users, jc);
		// 输出[{"username":"xx"},{"username":"yy"}]
		System.out.println(ja);
	}
}

2)XStream的用法(摘自XStream官网,看绿条部分即可)

① 初始化

15. AJAX9350

② 将JAVA对象转为XML

15. AJAX9368

③ XStream注解

  • @XStreamAlias(别名) 对类和变量设置别名
  • @XStreamAsAttribute 设置变量生成属性(默认生成XML元素)
  • @XStreamOmitField 设置变量不生成到XML
  • @XStreamImplicit(itemFieldName = “hobbies”) 设置集合类型变量别名

如上面案例中的Province类、City类就使用了XStream注解,如下所示:

15. AJAX9581

如果在程序中使用了XStream注解,在Xstream生成XML时需加上这句话:xStream.autodetectAnnotations(true);


留下一个回复

你的email不会被公开。