Struts2的Ajax支持是建立在Dojo和DWR基础之上的,这两个框架都提供了非常成熟的Ajax支持,包括非常优秀的页面控件和简单的DOM操作。其中Dojo提供了丰富的组件库和页面效果,而且提供了大量的函数来简化Ajax过程。DWR(Direct Web Remoting)则是Java领域一个著名的服务器端Ajax框架,借助于DWR的帮助,开发者可以直接在客户端页面通过JavaScript调用远程Java方法。简单的说就是DWR负责实现在JavaScript中调用远程Java方法,而Dojo则负责实现页面效果的显示。
Struts2的Ajax支持包括采用Ajax方式的输入校验,Ajax方式的输入校验可以在用户输入的同时完成校验,而且这种校验无需用户进行任何提交动作。只要用户输入完成,如果输入不满足输入要求,系统将自动显示校验提示。除此之外,Struts2的Ajax还允许以简单方式来异步提交表单请求,并提供了pub-sub的事件模型,并且提供了系列的Ajax标签来简化Ajax开发。
Ajax (Asynchronous Java/script And XML),即异步的JavaScript和XML技术,Ajax技术的关键在于异步发送请求。还有动态加载服务器响应的数据,使用Ajax技术的应用能避免频繁刷新页面,服务器响应的是数据,而不是整个页面内容。Ajax技术负责获取服务器数据,然后将服务器数据动态加载到浏览器中。
Ajax技术的核心是XMLHttpRequest对象,该对象在IE5中首次引入。整个Ajax应用的工作过程如下:
1,JavaScript脚本使用XMLHttpRequest对象向服务器发送请求。可以发送GET,POST请求。
2,JavaScript脚本使用XMLHttpRequest对象,解析服务器响应数据。
3,JavaScript脚本通过DOM,动态更新HTML页面,也可以为服务器响应数据增加CSS样式表,在当前页面的某个部分加以显示。
DOM (Document Object Model)是操作HTML和XML文件的一组API,它提供了文件的结构表述。通过使用DOM,可以采用编程方式操作文档结构,可以改变文档的内容。通过使用DOM,还可以增加文件的节点,属性及事件,从而提供对HTML页面的动态更新。
除此之外,Ajax技术可以使用XML文件,普通文本文件,JSON(JavaScript Object Notaion)作为数据交换的格式。
Struts2的Ajax支持是建立在Ajax主题基础上的,Ajax主题扩展了xhtml主题,增加了Ajax功能。
2,基于Ajax的输入校验 Ajax的输入校验不是客户端校验,而是服务器端校验,但这种服务器端校验是以异步方式进行的,浏览者无需显示提交请求,当浏览者输入完成后,系统自动完成校验。
2.1,配置DWR的核心Servlet
为了让DWR的核心Servlet起作用,必须在web.xml文件中配置核心Servlet,代码如下:
<servlet-name>dwr</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping>
<servlet-name>dwr</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> 此外还需要配置一个dwr.xml文件,将此文件放在WEB-INF/目录下,它的内容可以是固定的,代码如下:
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www./dwr/dwr10.dtd"> <dwr>
<allow> <create creator="new" javascript="validator"> <param name="class" value="org.apache.struts2.validators.DWRValidator"/> </create> <convert converter="bean" match="com.opensymphony.xwork2.ValidationAwareSupport"/> </allow> <signatures>
<![CDATA[ import java.util.Map; import org.apache.struts2.validators.DWRValidator; DWRValidator.doPost(String, String, Map<String, String>);
]]> </signatures> </dwr> 将DWR的jar包放在工程的WEB-INF/lib/目录下,它可以在DWR的官方网站上下载(http:///dwr),Struts2并不支持最新版本的DWR,因此我们只需要下载DWR1.1.3版本就可以了。 2.2,设置页面的Ajax主题
为了实现Ajax校验,还需要将表单设置成Ajax主题,并且设置validate="true",下面是regist.jsp页面的代码如下:
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@taglib prefix="s" uri="/struts-tags"%> <html><head><title>请输入您的注册信息</title>
<s:head theme="ajax"/></head> <body> <H1>请输入您的注册信息</H1> <s:form action="regist" theme="ajax" validate="true"> <s:textfield name="name" label="作者名"/><br> <s:textfield name="pass" label="密码"/> <s:textfield name="age" label="年龄"/> <s:textfield name="birth" label="生日"/> <s:submit value="注册"/> </s:form> </body></html>
上面的页面首先通过<s:head theme="ajax"/>标签导入了Ajax头,上面的表单被设置Ajax主题(这意味着该表单是一个远程表单),并且设置了validate="true"属性,一旦完成了上面设置后,当某个输入组件失去焦点时,系统会负责将输入内容发送到服务器端进行校验。
2.3,设置校验规则
对于Ajax校验的Action类,与基本校验的Action类并没有太大的区别,但是不要重写validate方法。如我们的Action类RegistAction.java,校验规则配置文件RegistAction-validation.xml。内容省略。
完成上面3步后,就可能使用Ajax的输入校验了。
3,使用Ajax表单
通过Struts2的Ajax表单的支持,我们可以采用异步方式提交表单请求。提交Ajax表单的请求参数后,当前页面不会提交,浏览者依然可以在当前页面继续自己的动作,直到服务器响应到达时,Struts2会自动加载服务器响应。
3.1设置表单的Ajax主题
使用Ajax技术有一个重要的用处:当前页面不会被提交,服务器响应的是数据,而不是内容。因此,服务器响应的数据只用于更新页面的部分内容。
为了指定该表单请求的响应更新的内容,我们应该在Ajax表单中使用Struts2标签来生成提交按钮。生成提交按钮时,可以为该按钮指定一个targets属性,用来指定该表单更新的所有部分,如果需要更新多个部分,多个部分之间以英文逗号(,)分隔。
在默认的情况下,当服务器响应到达时,默认将服务器响应输出在targets属性指定的HTML元素中,覆盖原来HTML元素里的内容。如果希望本页面可以执行服务器响应的JavaScript代码,则可以为该提交按钮指定<executeScript="true">属性,下面是remoteform.jsp页面的代码:
<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>远程表单</title> <s:head theme="ajax"/> </head> <body> <div id='show' style="background-color:#bbbbbb;width:360px;height:80px">原始静态文本</div> 使用表单请求的返回值来填充另一个Div。<br/> <s:form id='theForm1' cssStyle="border: 1px solid black;" action='AjaxTest' method='post' theme="ajax"> <s:textfield name='data' label="请输入您喜欢的图书"/> <s:submit value="修改上面的静态文本" targets="show"/> </s:form> 使用表单请求的返回值来填充本Form<br/> <s:form id='theForm2' cssStyle="border: 1px solid black;" action='AjaxTest' method='post' theme="ajax"> <s:textfield name='data' label="请输入您喜欢的图书"/> <s:submit value="修改Form本身" targets="theForm2"/> </s:form> 直接运行远程JavaScript(通过指定executeScripts="true")<br/> <s:form id='theForm3' cssStyle="border: 1px solid black;" action='Test3' method='post' theme="ajax"> <s:textfield name='data' label="请输入您喜欢的图书"/> <s:submit value="执行远程JS" executeScripts="true" targets="show"/> </s:form> </body> </html> 上面的页面中包含了三个表单,其中前两个表单都向AjaxTest.action发送请求,后一个Action向Test3.action发送请求,这两个Action包含的处理逻辑都非常简单。 3.2,实现Action
不管是用于处理Ajax方式请求的Action还是用于处理普通请求的Action,本身并没有太大的不同,下面是Action类的代码:
package lee;
import com.opensymphony.xwork2.Action;
import java.io.Serializable;
public class AjaxTestAction implements Action, Serializable { private static int counter = 0;
private String data; public long getServerTime() { return System.currentTimeMillis(); } public int getCount()
{ return ++counter; } public String getData()
{ return "服务器提示:" + data; } public void setData(String data)
{ this.data = data; } public String execute() throws Exception
{ return SUCCESS; } } 从上面的代码可以看出,这是一个普通的Action类。第二个Action并没有提供Action,而是直接返回JSP页面。struts.xml配置文件中的配置如下: <action name="AjaxTest" class="lee.AjaxTestAction">
<result>/AjaxResult.jsp</result> </action> <action name="Test3"> <result>/testjs.jsp</result> </action> 3.3,实现服务器响应页面
因为对于Ajax技术而言,服务器响应的不再是内容,而是数据,因此服务器內不应该是完整的页面,而应该是需要输出的数据,所以不要在服务器响应页面中使用完整的HTML页面标签,而是只提供需要输出的数据,AjaxResult.jsp页面的代码如下:
<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <% request.setAttribute("decorator", "none"); response.setHeader("Cache-Control","no-cache"); //HTTP 1.1 response.setHeader("Pragma","no-cache"); //HTTP 1.0 response.setDateHeader ("Expires", 0); //prevents caching at the proxy server %> 服务器计数器: <s:property value="count"/><br>
当前时间是:<s:property value="serverTime"/><br> 服务器返回的提示是:<s:property value="data"/> 从上面页面中可以看到,该页面不是一个完整的JSP/HTML页面,仅仅是数据片段,下面是testjs.jsp页面代码,它也一样没有包含完整的JSP/HTML页面标签,仅仅只有数据片段:
<%@ page contentType="text/html;charset=GBK" language="java" %>
<% request.setAttribute("decorator", "none"); response.setHeader("Cache-Control","no-cache"); //HTTP 1.1 response.setHeader("Pragma","no-cache"); //HTTP 1.0 response.setDateHeader ("Expires", 0); //prevents caching at the proxy server %> <script language="JavaScript" type="text/javascript">
alert('Spring2.0宝典'); </script> 轻量级J2EE企业应用实战 <script language="JavaScript" type="text/javascript"> alert('基于J2EE的Ajax宝典!'); </script> 该页面弹出了两个警告框,警告框结束后,可以看到上面ID为show的HTML元素的内容被更新了。
4,pub-sub的事件模型 pub-sub是public-subscribe两个单词的缩写,其意思就是发布--订阅,它提供了一种事件处理函数的注册方式。Struts2的pub-sub事件模型基本是采用了Dojo的pub-sub事件模型。
pub-sub事件模型提供了一种简化的事件监听方式,通过pub-sub事件模型,可以让一个JavaScript事件同时触发多个事件处理函数。当我们把一个事件(也可能是一个普通的函数)作为一个发布者注册到一个主题后,如果该事件被触发(普通函数被调用),则该主题下所有的事件处理函数都会被自动调用。 在Dojo中将一个事件注册到某个主题下的代码如下:
//将foo对象的bar方法注册到/refresh主题,事件主题是一个任意的字符串,并没有太多额外的要求
dojo.event.topic.publish("/refresh","foo","bar"); 将某个事件处理函数注册到某个主题下的代码如下:
//在/refresh主题下增加了一个匿名事件处理函数
dojo.event.topic.subscribe("/refresh",function(param1,param2){ //this function will be called everytime when "/refresh" is published }); Struts2在Dojo的pub-sub事件模型基础上,进行了简单的包装,它为大部分Ajax标签提供了如下两个属性:
listenTopics:指定系列事件主题名,多个主题之间以英文逗号(,)隔开。配置该元素的HTML元素将用于加载服务器响应。
notifyTopics:指定系列事件主题名,多个主题之间以英文逗号(,)隔开。配置该元素的HTML元素将会把事件发布到指定主题,发布主题时会传递3个参数:data, type, request。这3个参数代表了与服务器交互的内容,含义如下:
data:从服务器返回的数据。
type:与服务器交互的状态,只有3个值:before(交互之前) load(加载数据中) error(服务器响应出错)
request:代表请求对象本身。
因此,Struts2中的事件处理函数大致上都是如下形式的:
function(data, type, e) { ... ... }
4.1 pub-sub的例子
下面的代码中使用了一个按钮来发布事件,将事件发布到指定主题,并且通过JavaScript代码来为该主题增加一个事件处理函数。页面代码如下:
<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>pub-sub模型</title> <s:head theme="ajax"/> </head> <script type="text/javascript"> //为lee主题增加一个事件处理函数 dojo.event.topic.subscribe("/lee", function(data, type, e){ alert('正处于Dojo的异步交互过程中,类型是:'+type); alert(e); }); </script> <body> 简单pub-sub模型<br> <!-- notifyTopics属性设置该元素引发的事件发布到的主题 --> <s:submit type="submit" theme="ajax" value="提交" align="left" notifyTopics="/lee"/> </body> </html> 在上面代码中,定义"提交"按钮时,指定了notifyTopics="/lee"属性,该属性指定单击该按钮时,单击事件将被发布到/lee主题。 上面的代码是直接指定了事件处理函数作为/lee主题的订阅者,除此之外,我们也可以指定一个Struts2标签元素作为主题的订阅者,指定订阅者通过为Struts2标签listenTopics属性实现。如下面的JSP页面代码:
<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>pub-sub模型</title> <s:head theme="ajax"/> </head> <body> pub-sub模型<br> <s:url id="data" value="/data.action"/> <!-- notifyTopics属性设置该元素引发的事件发布到的主题 --> <s:submit type="submit" theme="ajax" value="更新" align="left" notifyTopics="/change"/> <!-- listenTopics属性设置该元素订阅的主题 --> <s:div theme="ajax" id="t1" cssStyle="background-color:#bbbbbb;width:360px;height:80px" listenTopics="/change" href="${data}"/> </body> </html> 上面的代码让一个"更新"按钮作为事件发布者,让一个div元素作为事件订阅者,意思就是每当单击"更新"按钮时,该按钮将会把单击事件发布到/change主题,而订阅者div元素的默认行为(更新自己的内容)被触发。本例子中的action直接转发到如下JSP页面: <%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <%=Math.random() > 0.5 ? "Spring2.0宝典" : "轻量级J2EE企业应用实战"%> 每次单击"更新"按钮时,将会引起页面中div元素内容的改变。
如果我们想阻止某个Ajax过程,则可以先将该Ajax标签的事件注册到某个事件主题下,再为该主题增加事件订阅者,并在该事件订阅者中取消该请求。例子如下:
<s:url id="ajaxTest" value="/AjaxTest.action" />
<!-- 将下面Ajax按钮的Ajax事件注册到/request事件主题下 --> <s:submit type="submit" theme="ajax" value="submit" notifyTopics="/request" href="%{ajaxTest}" /> 下面为/request事件主题增加订阅者
dojo.event.topic.subscribe("/request",function(data,type,request){ //取消request request.cancel=true; }); 通过上面的代码即可强行中止Ajax请求 |
|
来自: Semoon8088 > 《JAVA EE》