分享

Struts2的Ajax支持介绍

 Semoon8088 2013-05-11

1,Struts2的Ajax支持介绍

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请求

1,Struts2的Ajax支持介绍

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请求

1,Struts2的Ajax支持介绍
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请求
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多