Struts 2.2.3.1指南:核心开发指南(第部分)

Struts 2.2.3.1指南:核心开发指南(第一部分)

Struts 2.2.3.1指南
这个深入的指南主要关注框架的特定领域,例如:视图、核心框架、以及第三方的扩展。
第1章 核心开发指南
Struts 2使用了三个核心类型来处理请求:interceptor、action、result。每个类型都可以通过XML或注解来配置。
1.1 教程
请查看教程文档
1.2 使用Struts 2开发应用程序
1.2.1 概述
此框架文档是为积极主动的Web开发人员编写的,并假设他们已经了解了关于如何构建Java Web应用程序的工作知识。更多关于顶层的基本要素,查看关键技术入门。
1.2.1.1 Apache Struts 2框架概述
(1)Web浏览器请求一个资源(/mypage.action、/reports/myreport.pdf、等等);
(2)Filter Dispatcher查看这个请求并确定适当的Action;
(3)拦截器自动把通用功能应用于这个请求,例如:工作流、验证和文件上传的处理;
(4)执行Action方法,通常是把信息存入数据库或从数据库检索信息;
(5)Result把输出渲染到浏览器,可能为HTML、图片、PDF、或其它东西。
1.2.1.2 Struts 2标签概述
Struts标签帮助你以最少的代码创建富Web应用程序。通常,一个应用程序的大部分编码工作都在页面上。Struts标签通过减少代码来减少工作。
<% User user = ... %>
<form action="Profile_update.action" method="post">
    <table>
        <tr>
            <td align="right"><label>First name:</label></td>
            <td><input type="text" name="user.firstname" value="<%=user.getFirstname() %> /></td>
        </tr>
        <tr>
            <td><input type="radio" name="user.gender" value="0" id="user.gender0"
                <% if (user.getGender()==0) { %> checked="checked" %> } %> />
            <label for="user.gender0">Female</label>
        </tr>
    </table>
</form>
...

回顾以上标记,很容易看出为什么没有现代框架的帮助Java Web的开发是很困难的。到目前为止,我们只为两个控件进行了编码,还有六个控件需要编码。让我们使用Struts标签来重写并完成这个表单。
<s:actionerror/>
<s:form action="Profile_update" validate="true">
    <s:textfield label="Username" name="username"/>
    <s:password label="Password" name="password"/>
    <s:password label="(Repeat) Password" name="password2"/>
    <s:textfield label="Full Name" name="fullName"/>
    <s:textfield label="From Address" name="fromAddress"/>
    <s:textfield label="Reply To Address" name="replyToAddress"/>
    <s:submit value="Save" name="Save"/>
    <s:submit action="Register_cancel" value="Cancel" name="Cancel" 
        onclick="form.onsubmit=null"/>
</s:form>

Struts标签还支持验证和国际化等优秀特性。因此,不仅仅是减少了代码,而且还有更多功能。与两个传统控件具有差不多相同代码量的情况下,Struts标签可以创建一个包含八个控件的完整的数据输入表单。不仅仅减少了代码,而且代码更易于阅读和维护。
1.2.1.3 Struts配置概述
Web应用程序使用一个部署描述符来初始化资源,例如:过滤器和监听器。Web部署描述符的格式为XML文档,且名称是web.xml。Struts可以通过扫描在web.xml文件中声明的Java包中的类来初始化它的资源,你也可以通过struts.xml配置文件来完全控制配置。这些资源包括把输入指向服务端的Action类的Action映射,以及选择输出页面的Result类型。以下是一个典型的登录工作流的struts.xml配置:
<struts>
    <package name="default" extends="struts-default">
        <action name="Logon" class="mailreader2.Logon">
            <result name="input">/pages/Logon.jsp</result>
            <result name="cancel" type="redirectAction">Welcome</result>
            <result type="redirectAction">MainMenu</result>
            <result name="expired" type="chain">ChangePassword</result>
        </action>

        <action name="Logoff" class="mailreader2.Logoff">
            <result type="redirectAction">Welcome</result>
        </action>
    </package>
</struts>

框架提供了常用的默认值,因此我们可以马上开始使用Struts,开箱即用。如果需要的话,任何默认值都可以在应用程序的配置中被覆盖。
1.2.1.4 Struts MVC概述
Struts是一个MVC(模型-视图-控制器)框架。Struts提供了控制器和视图组件,以及提供了集成其它技术的模型。框架的控制器作为应用程序的模型与视图之间的桥梁。

为了更容易显示动态数据,框架包含了一个标记标签库。标签与框架的验证和国际化特性相互作用,确保输入正确性,以及输出是的本地化。标签库可以与JSP,FreeMarker,Velocity一起使用。当然,也可以使用其它标签库,JSTL,和Ajax,Struts标签可有可无。另外,还支持JavaServer Faces组件。

当接收到请求时,控制器会调用Action类。通过咨询模型(最好是用一个接口表示模型),Action类会检测或更新应用程序的状态。为了在模型与视图之间传递数据,属性可以放在Action类中,或者一个普通的JavaBean中。

更常用的是,模型会被表示为一个JavaBean对象图。模型应该承担重担,Action将作为一个总线管理模块或适配器。框架提供了一个复杂的,自动的类型转换来简化富领域对象与只支持文本的HTTP请求之间的数据传递。

Struts是可扩展的,框架部署的每个类都是基于接口的,我们提供了一个应用程序可能需要的所有基类,如果我们漏掉了一些东西,你可以很容易添加。我们提供了一个通用的框架,然而你仍然可以按照你自己的方式编写应用程序。
1.2.1.5 Struts是否是所有工程的最佳选择
Apache Struts 2根据行业标准以及成熟的设计模式,帮你创建一个可扩展的企业级应用程序的开发环境。如果你需要编写一个只有几个页面的简单应用程序,你可能会考虑只使用服务器页面的模式1解决方案。但是,如果你编写的是一个包含很多页面的复杂应用程序,需要不断地维护,那么Struts可以帮你。关于更多模式1或MVC/模式2哪个适合你,请查看了解JSP模型2框架。
1.2.1.6 平台要求
Struts 2需要:
(1)Servlet API 2.4
(2)JSP API 2.0
(3)Java 5
完整的需求列表,包括可选插件的依赖,请查看工程依赖。可以获取另一组用于Java 4的可选的JAR,请查看J4发布包。
1.2.2 Ajax
AJAX是异步JavaScript和XML(Asynchronous JavaScript and XML)的缩写。从本质上讲,一个JavaScript可以发出一个HTTP请求并直接更新页面的一部分,而不用通过传统的POST或GET以及刷新整个页面。更好的是,一个页面可以包含几个发出同步(异步)请求的JavaScript。

关键点在于,当一个Script发出一个Ajax请求(XHR)时,服务器并不知道它是来自Script的,并像处理其它请求一样处理这个请求。Ajax如此成功的一个因素在于它与现有的服务端技术一起工作的很好,包括Struts。

并不是Ajax请求有什么不同,而是Ajax响应。Ajax响应只会返回页面的一部分,而不是返回浏览器显示或重新显示的整个页面。响应可以采用的形式包括:XML、HTML、普通文本、其它脚本、或调用的脚本可能想要的其它任何形式。

Struts 1和Struts 2都可以返回任何响应类型。我们并不局限于转发到一个服务器页面。在Struts 1中,你只能这么做:
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Hello World!  This is an AJAX response from a Struts Action.");
out.flush();
return null;

在Struts 2中,我们可以使用一个Stream Result做同样的事情:
Struts 2 Stream result Action
package actions;

import java.io.InputStream;
import java.io.StringBufferInputStream;
import com.opensymphony.xwork2.ActionSupport;

public class TextResult extends ActionSupport  {
    private InputStream inputStream;
    public InputStream getInputStream() {
        return inputStream;
    }

    public String execute() throws Exception {
        inputStream = new StringBufferInputStream("Hello World! This is a text string response
            from a Struts 2 Action.");
        return SUCCESS;
    }
}

Struts 2 Configuring the TextResult Action
<action name="text-result" class="actions.TextResult">
    <result type="stream">
        <param name="contentType">text/html</param>
        <param name="inputName">inputStream</param>
    </result>
</action>

使用插件,Struts 2还可以返回一个JSON(JavaScript Object Notation)响应。

在客户端,有两种基本的策略,可以混合搭配。首先,你可以使用某些类型的JSP标签。在此你不需要了解非常多关于Ajax和JavaScript,taglib做了所有事情,你只需要了解如何使用taglib。标准的Struts 2 taglib包含了一些Ajax JSP标签,而且有一些第三方库可供选择,包括:
(1)Ajax Tags
(2)AjaxParts Taglib
(3)ColdTags Suite
(4)Prize Tags
(5)JSON-taglib
另外,你还可以在一个纯HTML页面上使用纯Ajax部件,使用一些库,例如:Dojo、JQuery、YUI,以及StreamResult或JSON插件。在此,限于篇幅,但是,作为一种语言,实际上你必须学习关于JavaScript的一些东西。
1.2.2.1 Ajax插件
虽然Struts可以与开箱即用的Ajax一起工作的很好,为了增值,一些基于Ajax的插件也可供选择。

Ajax标签插件
(1)Ajax Parts:AjaxParts Taglib(APT)是Java Web Parts(JWP)工程的一个组件(http://javawebparts.sourceforge.net),它允许在一个基于Java的Web应用中100%地声明AJAX功能,不需要JavaScript代码。
(2)Dojo:Ajax Tags Dojo插件代表了Struts 2.0的一个主题,对于Struts 2.1,Dojo标签已经被捆绑在一起作为一个插件。
(3)YUI:到目前为止Yahoo User Interface(YUI)插件只有一些标签可用,但是,YUI标签比Dojo版本更易于使用。
(4)jQuery:jQuery插件提供了Ajax功能以及一个基于jQuery JavaScript框架的JavaScript网格UI小部件。

其它Ajax插件
(1)Ajax File Upload:通过Ajax File Upload插件,我们可以把文件上传到服务器上,并异步监测它的处理过程。
(2)GWT:Google Web Toolkit插件把Struts 2 Action公开给GWT RPC机制。
(3)JSON:JSON插件把Action属性序列化为JSON,使其能够很容易响应JavaScript请求。

完整的Struts 2插件列表,请查看Struts Plugin Repository。
1.2.2.2 JSP中的Ajax请求
因为服务器页面最经常用于生产HTML,因此我们可以使用服务器页面来创建其它数据流类型。以下是一个示例:
book.jsp
<%@ page import="java.util.Iterator,
    java.util.List,
    com.esolaria.dojoex.Book,
    com.esolaria.dojoex.BookManager" %>
<%
    String bookIdStr = request.getParameter("bookId");
    int bookId = (bookIdStr == null || "".equals(bookIdStr.trim())) ?
        0 : Integer.parseInt(bookIdStr);
    Book book = BookManager.getBook(bookId);
    if (book != null) {
        out.println(book.toJSONString());
        System.out.println("itis: " + book.toJSONString());
    }
%>
在示例代码中,你可以使用System.out.println来返回一个JSON数据流来作为响应。关于这个技术的更多详细信息,请查看文章:使用Dojo和JSON来构建Ajax应用程序。
1.2.3 依赖注入
依赖注入移除了创建对象的责任,并把对象关联从对象本身转移到了工厂。工厂通常由一个IOC(控制反转)容器提供。关于更多IOC容器的概述,以及依赖注入模式,请查看Martin Fowler的文章。

在内部,框架使用自身的类似于Google Guice依赖注入容器,这两个最开始都是由Bob Lee开发的。插件可以用于把应用程序和其它IOC容器整合在一起,例如:Spring插件,Plexus插件。应用程序甚至还可以使用一个Google Guice的本地副本来满足依赖注入的需要。

Action仍然可以通过Spring的配置,以Spring插件的方式来实例化,但是Spring完全是可选的。

Struts 2不支持WebWork 2.1中使用的WebWork/XWork IOC容器。
1.2.4 性能分析
性能分析(Profiling)软件用于查找程序执行中的瓶颈。除了IDE提供的性能分析服务和标准的性能分析器外,框架还提供了内部的性能分析支持。
1.2.4.1 性能分析切面
Struts 2的性能分析切面包括以下内容:
(1)ActionContextCleanUp
(2)FreemarkerPageFilter
(3)DispatcherFilter
(3.1)Dispatcher
(3.1.1)创建DefaultActionProxy
(3.1.1.1)创建DefaultActionInvocation
(3.1.1.1.1)创建Action
(3.1.2)执行DefaultActionProxy
(3.1.2.1)调用DefaultActionInvocation
(3.1.2.1.1)调用Interceptors
(3.1.2.1.2)调用Action
(3.1.2.1.3)调用PreResultListener
(3.1.2.1.4)调用Result
1.2.4.2 用法
要启用性能分析,首先要确保把profiling拦截器应用于action,例如:
<action ... >
    ...
    <interceptor-ref name="profiling">
        <param name="profilingKey">profiling</param>
    </interceptor-ref>
    ...
</action>

然后使用以下方法中的一种来开启性能分析:
(1)通过系统属性来激活
-Dxwork.profile.activate=true
这可以在容器的启动脚本中这么做,例如在Tomcat的catalina.sh的CATALINA_OPTS中,或使用在Jetty中使用java -Dxwork.profile.activate=true -jar start.jar

(2)通过代码来激活
UtilTimerStack.setActivate(true);
这可以在以下方式中这么做:在一个静态块中,在一个使用lazy-init="false"的Spring Bean中,在一个使用init-on-startup数字值的Servlet中,在一个Filter或Listener的初始化方法中,等等。

(3)通过参数来激活
http://host:port/context/namespace/someAction.action?profiling=true

(4)修改激活参数的名称
把profiling拦截器的profilingKey属性设置位期望名称:
<action ... >
    ...
    <interceptor-ref name="profiling">
        <param name="profilingKey">profiling</param>
    </interceptor-ref>
    ...
</action>
通过一个参数,需要把struts.devMode设为true来激活性能分析。
1.2.4.3 过滤性能分析信息
通过如下系统属性,可以过滤出性能分析日志:
-Dxwork.profile.mintime=10000

通过这个xwork.profile.mintime属性,只有在执行时间超过xwork.profile.mintime系统属性指定的时间时,才能记录性能分析信息。如果你没有指定这个属性,就会把这个属性假设为0,因此会记录所有性能分析的信息。
1.2.4.4 编写性能分析代码
也可以在Web应用程序中扩展Struts 2提供的性能分析特性。

使用UtilTimerStack的push和pop
String logMessage = "Log message";
UtilTimerStack.push(logMessage);
try {
    // do some code
} finally {
    UtilTimerStack.pop(logMessage); // this needs to be the same text as above
}

使用UtilTimerStack的ProfileBlock模板
String result = UtilTimerStack.profile("purchaseItem: ",
    new UtilTimerStack.ProfilingBlock<String>() {
        public String doProfiling() {
            // do some code
            return "Ok";
        }
    });
1.2.4.5 性能分析的日志文件
性能分析结果是在名为com.opensymphony.xwork2.util.profiling.UtilTimerStack的日志记录器下,使用commons-logging来记录的。依靠底层的日志实现,假设是Log4j,可以直接把日志记录在不同的文件中,通过电子邮件发送给别人或存储在数据库中。
1.2.5 调试
现在的IDE对调试提供了很好的支持。此外,框架也提供了在运行时检查特定框架对象的支持。

Debugging拦截器提供了三种调试模式来深入了解页面背后的数据。xml模式把相关的框架对象格式化为XML文档。console模提供了接受运行时表达式入口的OGNL命令行。browser模式添加了一个用于显示值栈(Value Stack)中的对象的交互页面,这需要Dojo插件。

要使用调试,首选需要确保在struts.properties或struts.xml文件中,把struts.devMode属性设置为true。例如:
<constant name="struts.devMode" value="true" />

然后,无论什么时候,一个页面需要调试时,把?debug=xml或?debug=console或?debug=browser添加到URL中。
- <debug>
  <parameters />
- <context>
- <struts.actionMapping>
  <class>class org.apache.struts2.dispatcher.mapper.ActionMapping</class>
  <name>showcase</name>
  <namespace>/</namespace>
  </struts.actionMapping>
  <attr />
  <__devMode>true</__devMode>
  <report.conversion.errors>false</report.conversion.errors>
  </context>
  <request />
  <session />
- <valueStack>
- <value>
  <actionErrors />
  <actionMessages />
  <class>class com.opensymphony.xwork2.ActionSupport</class>
  <errorMessages />
  <errors />
  <fieldErrors />
- <locale>
  <ISO3Country>USA</ISO3Country>
  <ISO3Language>eng</ISO3Language>
  <class>class java.util.Locale</class>
  <country>US</country>
  <displayCountry>United States</displayCountry>
  <displayLanguage>English</displayLanguage>
  <displayName>English (United States)</displayName>
  <displayVariant />
  <language>en</language>
  <variant />
  </locale>
  </value>
- <value>
  <class>class com.opensymphony.xwork2.DefaultTextProvider</class>
  </value>
  </valueStack>
  </debug>

对于?debug=xml,使用IE或在FireFox中使用IE tab。
对于?debug=console,你可能需要放开阻止任何弹出窗口的程序。
1.2.6 开发模式
1.2.6.1 devMode
Struts 2中有一个名为devMode的设置(开发模式,可以在struts.properties中设置为true或false)。当启用这个设置时,Struts 2会提供额外的日志和调试信息,可以可以显著地加快开发。

你也可以在struts.xml文件中设置这个常量:<constant name="struts.devMode" value="true" />。这是首选方法,更多信息,请查看常量配置。
1.2.6.2 它做了什么
(1)启用后,每次请求Struts 2都会重新加载你的资源包,这意味着你可以修改.properties文件,保存,然后查看修改对下次请求的影响。注意:这个选项也可以通过struts.i18n.reload=true来进行单独设置。
(2)它也会在每次请求时重新加载xml配置文件struts.xml,验证文件,等等。这对于测试或微调配置来说是非常有用的,不需要每次都重新部署应用程序。
注意:这个选项也可以通过struts.configuration.xml.reload=true来进行单独设置。
(3)第三,或许这个设置是鲜为人知的,因此它是造成更多混乱的原因:它会提高调试或通常可以忽略的问题的错误级别。例如,当你提交了一个不能在someUnknownField action上设置的字段时,它通常会被忽略。然而,当你处于开发模式时,一个异常将会被抛出,告诉你提交了一个非法字段。这对于调试或测试大表单来说是非常有用的,但是这也会混淆,如果你依赖的请求参数没有被设置到action上,哪一个是你直接用于视图层的呢。警告:不好的实践,你应该总是验证来自web的输入。
1.2.6.3 不要忘记了
默认情况下,开发模式是被禁用的,因为对于每个请求整个配置都会被重新加载,所以它对性能有很大影响。