Struts2学习笔记(二)

一、Struts2流程

1.1 Struts2应用的开发步骤

1.1.1 在web.xml文件中定义核心Filter来拦截用户请求

  由于web.xml应用是基于请求/响应架构的应用,所以不管哪个MVC Web框架,都需要配置该框架的核心Servlet或Filter,这样才可以让框架介入Web应用中,虽然可以使用注解的方式配置,但是针对全局性的拦截器还是配置在web.xml文件中比较方便。示例如下:

<filter>
	<filter-name>struts2</filter-name>
	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<!-- 让Struts 2的核心Filter拦截所有请求 -->
<filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

1.1.2 定义处理用户请求的Action类

  这是所有MVC框架中必不可少的,因为这个Action就是MVC中的c,也就是控制器,该控制器负责调用Model里的方法来处理请求。

疑问一:Action并未接收到用户请求,它怎么能处理用户请求?

  MVC框架的底层机制是:核心Servlet或Filter接收到用户请求后,通常会对用户请求进行简单预处理,例如解析、封装参数等,然后通过反射来创建Action实例,并调用Action的指定方法(Struts1通常是execute,Struts2可以是任意方法)来处理用去请求。

疑问二:当Servlet或Filter拦截用户请求后,它如何知道创建哪个Action的实例呢?

两种解决方案:

  1. 利用配置文件:例如配置login.action对应使用LoginAction类。这就可以让MVC框架知道创建哪个Action的实例了。
  2. 利用约定:约定为xxx.action总是对应XxxAction类。如果核心控制器收到regist.action请求后,将会调用RegistAction类来处理用户请求。

  在MVC框架中,控制器实际上由两个部分共同组成,即拦截所有用户请求,处理请求的通用代码都由核心控制器完成,而实际的业务控制(诸如调用Model,返回处理结果等)则由Action处理。

1.1.3 配置Action

  对于Java领域的绝大部分MVC框架而言,都非常喜欢使用XML文件来配置管理,配置Action从而指定哪个请求对应哪个Action进行处理,从而让核心控制器根据该配置来创建合适的Action实例,并调用该Action的业务控制方法。

如下:

<!-- 指定action名称及对应的Action处理类 -->
<action name="" class="">
	...	
</action>

1.1.4 配置处理结果和物理试图资源之间的对应关系

  当Action处理完后,会返回一个字符串,我们可以根据字符串的内容来确定跳转页面。通常这个返回真的字符串名称称为逻辑视图名,这个逻辑视图名需要和指定物理试图资源关联才有价值。如下:

<action name="" class="">
	<result name=""> /WEB-INF/...</result>
</action>

1.1.5 编写视图资源

  如果Action需要把一些数据传给试图资源,则可以借助于OGNL表达式。

1.2 Struts2的流程

  下面使用流程图来简单演示:

  上面示例中,我们配置Struts2核心拦截器(FilterDispatcher)为StrutsPrepareAndExecuteFileter和XxxAction共同构成Struts2的控制器,常常把StrutsPrepareAndExecuteFileter称为核心控制器和XxxAction业务控制器。

  业务控制器通常并不与物理试图关联,这种做法提供了很好的解耦。业务控制器只负责返回处理结果,而该处理结果与怎样的视图关联,依然由核心控制器来决定。这样做的好处是:如果有一天需要将某个某个试图名映射到不同试图资源,这就无须修改XXXAction的代码,而是只需修改配置文件即可。

  在Struts2框架的控制下,用户请求不再向JSP页面发送,而是由核心控制器调用JSP页面来生成响应,此处的调用并不是直接调用,而是将请求forward指定JSP页面。

二、Struts2的常规配置

  Struts2的默认配置文件名为struts.xml,该文件应该放在Web应用的类加载路径下,通常就是放在WEB-INF/classes路径下。struts.xml配置文件最大的作用就是配置Action和请求之间的对应关系,并配置逻辑视图名和物理视图资源之间的对应关系,除此之外,Struts.xml文件还有一些额外的功能,例如Bean配置、配置常量、导入其他配置文件等。

2.1 常量配置

  Struts2除了可使用struts.xml文件来管理配置之外,还可使用struts.properties文件来管理常量,该文件定义了Struts2框架的大量常量,开发者可以通过改变这些常量来满足应用的需求。struts.properties文件是一个标准的Properties文件,该文件包含了系列的key-value对。Struts2的常量相当于对于Struts2应用整体起作用,因此Struts2常量常常也被称为Struts2属性。

  Struts2常量配置都包含有默认配置,该默认配置在struts2-core-*.jar中org.apache.struts2路径下的default.properties文件中。

下面针对可能使用到的常量做简单说明:

  • struts.locale:指定Web应用的默认Local,默认的Local是en_US。
  • struts.il8n.encoding:指定Web应用的默认编码集。该常量对于处理中文请求参数非常有用,对于获取中文请求参数值,应该讲该常量值设置为GBK或者GB2312,默认值为UTF-8。(相当于执行了HttpServletRequest的setCharacterEncoding()方法)
  • struts.action.extension:该常量指定需要Struts2处理的请求后缀,该常量的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。
  • struts.serve.static.browserCache:该常量设置浏览器是否缓存静态内容。当应用处于开发阶段时,如果希望每次请求都获得服务器的最新响应,则可设置该常量为false。
  • struts.devMode:该常量设置Struts2应用是否使用开发模式。如果设置该常量为true,则可以在应用出错时显示更多、更友好的出错提示。该常量只接受true和false两个值,该常量默认值为false。通常,应用在开发阶段,将该常量设置为true;当进入产品发布阶段后,则该常量设置为false。注:当把该常量设为true之后,相当于把struts.il8n.reload、struts.configuration.xml.reload两个常量都设为true。
  • struts.il8n.reload:该常量设置是否每次HTTP请求到达时,系统都重新加载资源文件。默认值为false。在开发阶段将该常量设置为true会更有利于开发,但在产品发布阶段应将常量设置为false。
  • struts.configuration.xml.reload:该常量设置当struts.xml文件改变后,系统是否自动重新加载该文件。该常量的默认值为false。
  • struts.custom.il8n.resources:该常量指定Struts2应用所需要的国际化资源文件,如果有多个国际化资源文件,则多个资源文件的文件名以英文逗号(,)隔开。
  • struts.objectFactory=spring:把Struts2的类的生成交给Spring完成(用于集成Struts2与Spring框架)。
  • struts.enable.DynamicMethodInvocation:设置Struts2是否支持动态方法调用,该常量的默认值是true。
  • struts.enable.SlashesInActionNames:设置Struts2是否允许在Action名中使用斜线,默认值时false。
  • struts.ognl.allowStaticMethodAccess:该常量设置是否允许在OGNL表达式中调用静态方法。该常量的默认值为false。

  Struts2默认会加载类加载路径下的struts.xml、struts-default.xml、struts-plugin.xml三个文件,其中struts.xml是开发者定义的默认配置文件,struts-default.xml是struts2框架自带的配置文件,而struts-plugin.xml则是Struts2插件的默认配置文件。

  在*.xml文件中配置常量的方式如下:

<struts>
	<!-- 通过constant元素配置Struts2的属性 -->
	<constant name="" value=""/>	
	...
</struts>

  通常推荐在struts.xml文件中定义Struts2属性,而不是在struts.properties文件中定义Struts2属性。之所以保留使用struts.properties文件定义Struts2属性的方式,主要是为了保持与WebWork的向后兼容性。

通常,Struts框架按如下搜索顺序加载Struts2常量:

  • struts-default.xml:该文件保存在struts2-core-*.jar文件中
  • struts-plugin.xml:该文件保存在struts2-Xxx-*.jar等Struts2插件JAR文件中
  • struts.xml:该文件是Web应用默认的Struts2配置文件
  • struts.properties:该文件是Struts2默认的配置文件
  • web.xml:该文件是Web应用的配置文件

  上面指定了Struts2框架搜索常量顺序,如果在多个文件中配置了同一个Struts2常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值。

  struts.xml文件是整个Struts2框架的核心,下面将展示一份完整的struts.xml文件骨架,这个文件没有实际意义,如下:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE struts PUBLIC 
		"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
		"http://struts.apache.org/dtds/struts-2.3.dtd">
<!-- struts是Struts 2配置文件的根元素 -->
<struts>
  <!-- 下面的元素可以出现0次,或者无限多次 -->
  <constant name="" value="" />
  <!-- 下面的元素可以出现0次,或者无限多次 -->
  <bean type="" name="" class="" scope="" static="" optional="" />
  <!-- 下面的元素可以出现0次,或者无限多次 -->
  <include file="" />
  <!-- package元素是Struts配置文件的核心,该元素可以出现0次,或者无限多次 -->
  <package name="必填的包名" extends="" namespace="" abstract="" externalReferenceResolver="">
    <!-- 该元素可以出现,也可以不出现,最多出现一次 -->
    <result-types>
      <!-- 该元素必须出现,可以出现无限多次 -->
      <result-type name="" class="" default="true|false">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </result-type>
    </result-types>
    <!-- 该元素可以出现,也可以不出现,最多出现一次 -->
    <interceptors>
      <!-- 该元素的interceptor元素和interceptor-stack至少出现其中之一,也可以二者都出现 -->
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <interceptor name="" class="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </interceptor>
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <interceptor-stack name="">
        <!-- 该元素必须出现,可以出现无限多次 -->
        <interceptor-ref name="">
          <!-- 下面的元素可以出现0次,或者无限多次 -->
          <param name="参数名">参数值</param>
        </interceptor-ref>
      </interceptor-stack>
    </interceptors>
    <!-- 下面的元素可以出现0次,最多出现一次 -->
    <default-interceptor-ref name="">
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <param name="参数名">参数值</param>
    </default-interceptor-ref>
    <!-- 下面的元素可以出现0次,最多出现一次 -->
    <default-action-ref name="">
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <param name="参数名">参数值</param>
    </default-action-ref>
    <!-- 下面的元素可以出现0次,最多出现一次 -->
    <default-class-ref class="">...</default-class-ref>
    <!-- 下面的元素可以出现0次,最多出现一次 -->
    <global-results>
      <!-- 该元素必须出现,可以出现无限多次 -->
      <result name="" type="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </result>
    </global-results>
    <!-- 下面的元素可以出现0次,或者无限多次 -->
    <global-exception-mappings>
      <!-- 该元素必须出现,可以出现无限多次 -->
      <exception-mapping name="" exception="" result="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </exception-mapping>
    </global-exception-mappings>
    <!-- 定义Action,可以出现0次,也可以无限多次 -->
    <action name="" class="" method="" converter="">
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <param name="参数名">参数值</param>
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <result name="" type="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </result>
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <interceptor-ref name="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </interceptor-ref>
      <!-- 下面的元素可以出现0次,或者无限多次 -->
      <exception-mapping name="" exception="" result="">
        <!-- 下面的元素可以出现0次,或者无限多次 -->
        <param name="参数名">参数值</param>
      </exception-mapping>
    </action>
  </package>
  <!-- 可以出现0次到1次 -->
  <unknown-handler-stack>
    <unknown-handler-ref name="处理器名">...</unknown-handler-ref>
  </unknown-handler-stack>
</struts>

2.2 包含其他配置文件

  如果项目非常大,那么把所有的配置都写在struts.xml文件中,将导致该文件非常臃肿,为了提高其可读性,可以将一个struts.xml配置文件分解成多个配置文件然后在struts.xml文件中包含其他配置文件。

  我们可以在struts.xml文件中添加:<include file="" /> 。注意包含的XML文件也必须包含与struts.xml一样的DTD信息、Struts2配置文件根元素等信息,struts.xml文件由Struts2框架负责加载,从而可以将所有配置信息都加载进来。

三、Action

  Struts2不要求Action类继承任何的Struts2基类,或者实现任何Struts2接口,在这种设计方式下,Struts2的Action类是一个普通的POJO(通常应该包含一个无参数的execute方法),从而有很好的代码复用性。

3.1 Action接口和ActionSupport基类

  为了让用户开发的Action类更规范,Struts2提供了一个Action接口,这个接口定义了Struts2的Action处理类应该实现的规范。下面是标准Action接口的代码:

public interface Action{
	//定义Action接口里包含的一些结果字符串
	public static final String ERROR="error";
	public static final String INPUT="input";
	public static final String LOGIN="login";
	public static final String NONE="none";
	public static final String SUCCESS="success";
	//定义处理用户请求的execute方法
	public String execute() throws Exception;
}

  不过实际开发的过程中,一般继承ActionSupport类而不是Action接口,ActionSupport提供了许多默认方法,这些默认方法包括获取国际化信息的方法、数据校验方法、默认的处理用户请求的方法等。同样ActionSupport继承了Action等许多接口,因此开发时使用ActionSupport比较方便。

3.2 Action访问Servlet API

  Struts2的Action没有与任何Servlet API耦合,由于Action类不在于Servlet API耦合,从而能更轻松地测试Action。但对于Web应用的控制器而言,不访问Servlet API几乎是不可能的。Web应用中通常需要访问的Servlet API就是HttpServletRequest、HttpSession和ServletContext,这三个接口分别代表JSP内置对象中的request、session和application。

  Struts2提供了一个ActionContext类,Struts2的Action可以通过该类访问ServletAPI。下面是其常用方法:

  • Object get(Object key):该方法类似于调用HttpServletRequest的getAttribute(String name)方法
  • Map getApplication():返回一个Map对象,该对象模拟了该应用的ServletContext实例
  • static ActionContext getContext():静态方法,获取系统的ActionContext实例
  • Map getParameter():获取所有的请求参数。类似于调用HttpServletRequest对象的getParameterMap()方法
  • Map getSession():返回一个Map对象,该Map对象模拟了HttpSession实例
  • void setApplication(Map application):直接传入一个Map实例,将该Map实例里的key-value对转换成application的属性名、属性值
  • void setSession(Map session):直接传入一个Map实例,将该Map实例里的key-value对转换成session的属性名、属性值

3.3 Action直接访问Servlet API

  虽然Struts2提供了ActionContext来访问Servlet API,但这种访问毕竟不是直接获得Servlet API的实例。为了在Action中直接访问ServletAPI,Struts2还提供了如下介个接口:

  • ServletContextAware:实现该接口的Action可以直接访问Web应用的ServletContext实例
  • ServletRequestAware:实现该接口的Action可以直接访问用户请求的HttpServletRequest实例
  • ServletResponseAware:实现该接口的Action可以直接访问服务器响应的HttpServletResponse实例

3.4 使用ServletActionContext访问Servlet API

  为了能直接访问Servlet API,Struts2还提供了一个ServletActionContext工具类,这个类包含了如下几个静态方法:

  • static PageContext getPageContext():取得Web应用的PageContext对象
  • static HttpServletRequest getRequest():取得Web应用的HttpServletRequest 对象
  • static HttpServletResponse getResponse():取得Web应用的HttpServletResponse 对象
  • static ServletContext getServletContext():取得Web应用的ServletContext对象

  借助于ServletActionContext类的帮助,开发者也可以在Action中访问Servlet API,并可避免Action类需要实现XxxAware接口——虽然如此,但该Action依然与Servlet API直接解耦,一样不利于高层次的解耦。

  借助于ServletActionContext工具类的帮助,Action能以更简单的方式来访问Servlet API。

四、配置Action

4.1 包和命名空间

  个人理解:包与Java包的概念差不多,我们可以将action看做是Java源文件一样,用来解决其命名冲突问题,而命名空间是为了区分网络地址冲突的方法。

  Struts2使用包来组织Action,Struts2框架使用包来管理Action和拦截器,每个包就是多个Action、多个拦截器、多个拦截器引用的集合。

包的定义使用<package ...>元素,其中包含如下几个属性:

  • name:必需元素,该属性是引用该包的唯一标识。
  • extends:可选属性,该属性指定该包继承其他包,从而继承其他包重的Action定义、拦截器定义等。
  • namespace:可选属性,该属性定义该包对应的命名空间。
  • abstract:可选属性,它指定该包是否是否为一个抽象包。抽象包中不能包含Action定义。

注意:因为Struts2的配置文件是从上到下处理的,所以父包应该在子包前面定义。

  如果我们定义一个包,而没有指定命名空间或继承struts-default其作用是完全相同的,而struts-default其实就是一个抽象包,这个抽象包包含在struts2-core-*.jar文件中的struts-default.xml文件。

  Struts2之所以提供命名空间的功能,主要为了处理同一个Web应用中包含同名Action的情形。Struts2不支持为单独的Action设置命名空间,而是通过为包指定namespace属性来为包下面的所有Action指定共同的命名空间,如果没有指定namespace属性,则该包下的所有Action处于默认的包空间下。

  当某个包指定了命名空间后,该包下所有的Action处理的URL应该是命名空间+Action名。

  Struts2还可以显示指定根命名空间,通过设置某个包的namespace=“/”来指定根命名空间。

  默认命名空间非常特殊,当我们在包中没有指定namespace属性,则指定该包在默认命名空间中。如果我们在请求的时候指定了特定的命名空间,则会先在其命名空间下查找其action,如果没有找到就会在默认命名空间再次查找。如果我们没有指定命名空间直接写action,则会先到根命名空间下进行查找,当没有的情况下,会到默认命名空间查找。而根命名空间下的Action只处理根命名空间下的Action请求,这也是根命名空间和默认命名空间的区别。

  总的来说,所有请求会先到相应的命名空间或者根命名空间查找,当无法查找到对应的Action的时候,就会到默认命名空间进行查找,也就是说URL中指定的命名空间是没有定义的情况下,也可以到默认命名空间下进行查找。

注:如果我们在struts.xml文件中定义了多个相同的命名空间,则会以最后一个命名空间为准。

4.2 Action的基本配置

  定义Action时,至少需要指定Action的name属性,该name属性既是该Action的名字,也指定了该Action所处理的请求的URL。如果请求URL包含.ation后缀,则应该去掉.action后缀再匹配。除此之外,通常还需要为action元素指定一个class属性,其中class属性指定了该Action的实现类,如果没有指定的话,系统则默认使用系统的ActionSupport类。

  Action只是一个逻辑控制器,它并不直接对浏览者生成任何响应。因此,Action处理完用户请求后,Action需要将指定的视图资源呈现给用户。因此配置Action时应该配置逻辑视图和物理视图资源之间的对应关系。

  配置逻辑视图和物理视图之间的映射关系是通过<result...>元素指定,而其包含一个name属性来指定Action处理类返回值所匹配的处理方式,需要注意的是:如果为name属性分配一个带点(.)或者带中画线(-)的值,例如my.user或者my-action等,则可能引发一些未知异常,因此,不推荐在Action的name属性值中使用点和中画线。

4.3 使用Action的动态方法调用

  Struts2提供了DispatchAction,从而允许一个Action内包含多个控制处理逻辑,这样我们就可以针对同一个表单提交上来的相同内容,进行不同的处理。其实我们完全可以修改URL从而实现调用Action的不同方法,而在表单提交的时候我们如何进行修改响应的Action内容,这个时候我们就需要借助于JavaScript来进行处理,这种方式称为DMI(Dynamic Method Invocation,动态方法调用),动态方法调用是指表单元素的action并不是直接等于Action的名字,而是以如下形式来指定表单的action属性。

其实就是改变form表单的action属性如下:

<!-- action属性为actionName!methodName的形式,其中methodName就是其action中包含的方法 -->
action="actionName!methodName"

具体的后台代码就不举例了,前台代码如下:

<form action="login" method="post">
	<table>
		<tr>
			<td>用户名:</td>
			<td><input type="text" name="username" /></td>
		</tr>
		<tr>
			<td>密  码:</td>
			<td><input type="text" name="password" /></td>
		</tr>
		<tr>
			<td><input type="submit" value="登录"
				onclick="this.form.action='login';" /></td>
			<td><input type="submit" value="注册" onclick="regist();" /></td>
		</tr>
	</table>
</form>
//动态改变提交的Action
<script type="text/javascript">
	function regist() {
		// 获取页面的第一个表单
		targetForm = document.forms[0];
		// 动态修改表单的action属性
		targetForm.action = "login!regist";
	}
</script>

注意:若要使用动态方法调用必须设置Struts2允许动态方法调用,也就是struts.enable.DynamicMethodInvocation=true(这是默认情况),不过Struts2动态犯法调用存在一些安全问题,应该尽量少用,可以使用下面的方式最好。

4.4 指定method属性及使用通配符

  指定struts.xml配置文件的action元素添加method方法,这样调用该action的时候会调用该action对应处理类的对应方法,这样就可以将一个Action类配置成多个逻辑的Action,当然不指定,则会默认调用该action处理类的execute方法。
  针对上面的一个示例,我们可以把JavaScript代码做一下略微调整,从而不需要指定需要调用action的方法了。

  使用通配符的方式:在配置<action...>允许在指定name属性时使用模式字符串(即“*”代表一个或多个任意字符),接下来就可以在class、method属性及<result...>子元素中使用{N}的形式来代表前面第N个星号(*)所匹配的子串。

  对于使用Struts2框架的应用而言,尽量不要让超链接直接链接到某个视图资源,因为这种方式增加了额外的风险。我们尽量拦截所有的请求,从而使用框架来处理用户请求,即使只是简单的超链接。

  对于只是简单的超链接请求,可以通过定义name="*"的Action(该Action应该放在最后定义)实现。除此之外,Struts2还允许在容器中定义一个默认的Action,当用户请求的URL在容器中找不到对应的Action是,系统将使用默认Action来处理用户请求。

疑问解析:如果当用户请求的URL同时匹配多个Action时,究竟由哪个Action来处理用户请求呢?

  1. 如果不包含通配符的Action匹配成功,则直接使用,而不去使用包含通配符Action
  2. 如果包含通配符时会按照从上到下的定义的顺序来进行匹配,如果在上面匹配成功,则直接使用,这也是为什么name=“*”的Action需要放在最下面的原因,如果放在最上面,则Struts2将使用该Action来处理所有希望使用模式匹配的请求。

简而言之:除非请求的URL与Action的name属性绝对相同,否则将按先后顺序来决定由哪个Action来处理用户请求。

4.5 配置默认Action

  为了让Struts2的Action可以接管用户请求,可以配置name=“*”的Action。除此之外,Struts2还支持配置默认Action,当用户请求找不到对应的Action时,系统默认的Action即将处理用户请求。

  配置默认Action通过<default-action-ref...>元素完成,如下:

<!-- 配置一个package元素 -->
<package name="" extends="">
	...
	<!-- 配置一个默认Action,默认Action为simpleViewResultAction -->	
	<default-action-ref name="simpleViewResultAction"/>
	...
	<!-- 通过action元素配置默认的Action -->
	<action name="simpleViewResultAction" class="">
		<result...></result>	
	</action>
	...
</package>

注:将默认Action配置在默认命名空间里就可以让该Action处理所有用户请求,因为默认命名空间的Action可以处理任何命名空间的请求。

4.6 配置Action的默认处理类

  因为<action...>配置中的class并不是必须的,因此如果没有指定action对应的处理类的话,系统默认使用ActionSupport作为Action处理类,实际上我们也可以进行自己配置Action的默认处理类,配置Action的默认处理类使用<default-class-ref..>元素,配置该元素时,只需要指定一个class属性,该class属性指定的类就是Action的默认处理类。

五、配置处理结果

5.1 Action处理结果

  Action处理完用户请求后,将返回一个普通字符串,整个普通字符串就是一个逻辑视图名。Struts2通过配置逻辑视图名和物理视图之间的映射关系,一旦系统收到Action返回的某个逻辑视图名,系统就会把对应的物理视图呈献给浏览者。除此之外,Struts2还支持多种结果映射:Struts2框架处理结果转向实际资源时,实际资源不仅可以是JSP视图资源,也可以是Freemarker视图资源,甚至可以将请求转发给下一个Action进行处理,形成Action的链式处理。

5.2 配置结果

  Struts2的Action处理用户请求结束后,返回一个普通字符串——逻辑视图名,必须在struts.xml文件中完成逻辑试图和物理视图自愿的映射,才可让系统转到实际的试图资源。

  Struts2在struts.xml文件中使用<result..>元素来配置结果,根据该元素所在元素位置的不同,Struts2提供了两种结果:

  • 局部结果:将<result..>元素作为<action...>元素的子元素配置
  • 全局结果:将<result..>元素作为<global-results...>元素的子元素配置

  <result..>元素包含两个属性name和type,name指定所配置的逻辑视图名,type指定结果类型name属性也有默认值,默认值为success,比较简单,就不赘述了。而type进行详细分析。

5.3 Struts2支持的结果类型

  当一个Action处理用户请求结束后,仅仅返回一个字符串,这个字符串是逻辑视图名,但该逻辑视图并未与任何的视图技术及任何的视图资源关联——直到struts.xml文件中配置物理逻辑视图资源。结果类型决定了Action处理结束后,下一步将调用相应的视图资源来呈现处理结果。

  Struts2的结果类型要求实现com.opensymphony.xwork2.Result,这个结果是所有结果类型的通用接口。如果想实现自己的结果类型,也需要提供一个实现该接口的类,并且在struts.xml文件中配置该结果类型。

  Struts2默认提供了一系列的结果类型,下面是struts-default.xml配置文件的配置片段,该文件保存在struts2-core-*.jar文件的根目录下,如下:

<result-types>
    <!-- Action链式处理的结果类型 -->
    <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
    <!-- 用于与JSP整合的结果类型 -->
    <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
    <!-- 用于与FreeMarker整合的结果类型 -->
    <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
    <!-- 用于控制特殊的HTTP行为的结果类型 -->
    <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
    <!-- 用于直接跳转到其他URL的结果类型 -->
    <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
    <!-- 用于直接跳转到其他Action的结果类型 -->
    <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
    <!-- 用于向浏览器返回一个InputStream的结果类型 -->
    <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
    <!-- 用于整合Velocity的结果类型 -->
    <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
    <!-- 用于整合XML/XSLT的结果类型 -->
    <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
    <!-- 用于显示某个页面原始代码的结果类型 -->
    <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
    <!-- 暂不理解... -->
    <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
</result-types>

  除此之外,还可以在struts2-jfreechart-plugin-*.jar的struts-plugin.xml文件中看到如下配置片段:

<result-types>
	<result-type name="chart" class="org.apache.struts2.dispatcher.ChartResult">
		<param name="height">150</param>
		<param name="width">200</param>
	</result-type>
</result-types>

  不同的结果类型支持不同的参数,其中配置dispatch结果类型是,指定了default=“true”属性,该属性表明该结果类型是默认的结果类型,这也就意味着如果省略了type属性,默认type属性为dispatcher的原因。
struts2內建的支持结果类型(不包含插件支持的)如下:

  • chain结果类型:Action链式处理的结果类型
  • dispatcher结果类型:用于指定使用JSP作为视图的结果类型
  • freemarker结果类型:用于指定使用FreeMarker模板作为视图的结果类型
  • httpheader结果类型:用于控制特殊的HTTP行为的结果类型
  • redirect结果类型:用于直接跳转到其他URL的结果类型
  • redirectAction结果类型:用于直接跳转到其他Action的结果类型
  • stream结果类型:用于向浏览器返回一个InputStream(一般用于文件下载)
  • velocity结果类型:用于指定使用Velocity模板作为视图的结果类型
  • xslt结果类型:用于与XML/XSLT整合的结果类型
  • plainText结果类型:用于显示某个页面的原始代码的结果类型

5.3 动态结果

  动态结果的含义是指在实际视图资源是使用了表达式语法,通过这种语法可以允许Action处理完用户请求后,动态转入实际的视图资源,这部分上面也进行讲述。前面介绍Action配置时,可以通过在Action的name属性中使用通配符,在class或method属性中使用{N}表达式。通过这种方式,允许Struts2根据请求来动态决定Action的处理类,以及动态决定处理方法。

5.4 Action属性值决定物理视图资源

  配置<result...>元素时,不仅可以使用${N}表达式形式来指定视图资源,还可以使用${属性名}的方式来指定视图资源。在后面这种配置方式下,${属性名}里的属性名就是对应Action实例里的属性。而且,不仅允许使用这种简单表达式形式,还可以使用完全的OGNL表达式,即使用这种形式:${属性名.属性名.属性名...},示例如下:

action配置如下:

<action name="MyAction" class="org.crazyit.app.action.MyAction">
	<!-- 配置Result,使用OGNL表达式来指定视图资源 -->
	<result>/WEB-INF/content/${target}.jsp</result>		
</action>

form中配置如下:

<input type="text" name="target" />

根据表单的i中提交的文本内容,来决定action处理类返回success时转向的jsp页面。

5.5 全局结果

  前面已经提及,可以配置全局的<result...>,只需要在<global-results...>元素中配置<result...>元素,全局结果将对所有的Action都有效。

注:如果一个Action里包含了与全局结果里同名的结果,则Action里的局部Result会覆盖全局Result。也就是说当Action处理用户请求结束后,会首先在本Action里的局部结果里搜索,只有在Action里的局部结果里找不到逻辑视图对应的结果,才会到全局结果里搜索。

5.6 使用PreResultListener

  PreResultListener是一个监听器接口,它可以在Action完成控制处理之后,系统转入实际的物理视图之间被回调。

  Struts2应用可由Action、拦截器添加PreResultListener监听器,添加PreResultListener监听器通过ActionInvocation的addPreResultListener()方法完成。一旦为Action添加了PreResultListener监听器,该监听器就可以在应用转入实际物理视图之前回调该监听器的beforeResult()方法;一旦为拦截器添加了PreResultListener监听器,该监听器会对该拦截器所拦截的所有Action都起作用。
示例如下:

public class LoginRegistAction extends ActionSupport {
	private static final long serialVersionUID = 1L;
	// 封装用户请求参数的两个成员变量
	private String username;
	private String password;

	// username的setter和getter方法
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	// password的getter和setter方法
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	// Action包含的注册控制逻辑
	public String regist() throws Exception {
		ActionContext.getContext().getSession().put("user", getUsername());
		addActionMessage("恭喜您," + getUsername() + ",您已经注册成功!");
		return SUCCESS;
	}

	// Action默认包含的控制逻辑
	public String execute() throws Exception {
		ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
		invocation.addPreResultListener(new PreResultListener() {
			public void beforeResult(ActionInvocation invocation, String resultCode) {
				System.out.println("返回的逻辑视图名字为:" + resultCode);
				// 在返回Result之前加入一个额外的数据。
				invocation.getInvocationContext().put("extra", new java.util.Date() + "由" + resultCode + "逻辑视图名转入");
				// 也可加入日志等
			}
		});
		if (getUsername().equals("admin") && getPassword().equals("admin")) {
			ActionContext.getContext().getSession().put("user", getUsername());
			addActionMessage("欢迎," + getUsername() + ",您已经登录成功!");
			return SUCCESS;
		}
		return ERROR;
	}
}

struts.xml文件如下:

<struts>
	<package name="lee" extends="struts-default">
		<!-- 配置login Action,处理类为LoginRegistAction
			默认使用execute方法处理请求-->
		<action name="login" class="org.crazyit.app.action.LoginRegistAction">
			<!-- 定义逻辑视图和物理视图之间的映射关系 -->
			<result name="error">/WEB-INF/content/error.jsp</result>
			<result>/WEB-INF/content/welcome.jsp</result>
		</action>
		<!-- 配置regist Action,处理类为LoginRegistAction
			指定使用regist方法处理请求-->
		<action name="regist" class="org.crazyit.app.action.LoginRegistAction"
			method="regist">
			<!-- 定义逻辑视图和物理视图之间的映射关系 -->
			<result name="error">/WEB-INF/content/error.jsp</result>
			<result>/WEB-INF/content/welcome.jsp</result>
		</action>
		<action name="*">
			<result>/WEB-INF/content/{1}.jsp</result>
		</action>
	</package>
</struts>

loginForm.jsp

<body>
	<form action="login" method="post">
		<table width="300" align="center">
			<tr>
				<td>用户名:</td>
				<td><input type="text" name="username" /></td>
			</tr>
			<tr>
				<td>密  码:</td>
				<td><input type="text" name="password" /></td>
			</tr>
			<tr>
				<td><input type="submit" value="登录"
					onclick="this.form.action='login';" /></td>
				<td><input type="submit" value="注册" onclick="regist();" /></td>
			</tr>
		</table>
	</form>
	<script type="text/javascript">
		function regist() {
			// 获取页面的第一个表单
			targetForm = document.forms[0];
			// 动态修改表单的action属性
			targetForm.action = "regist";
		}
	</script>
</body>

welcom.jsp

<body>
	<s:actionmessage/>
	PreResultListener添加的数据:<s:property value="extra"/>
</body>

六、配置Struts2的异常处理

 

  我们可以在Action的execute()方法中使用try-catch块来捕捉异常,当捕捉到指定异常时,系统返回对应逻辑视图名——这种处理方式完全是手动处理异常,非常烦琐,而且可维护性不好,如果有一天改变异常处理流程,则必须修改Action代码。

6.1 Struts2的异常处理机制

  Struts2允许通过struts.xml文件来配置异常的处理。我们查看Action接口里的execute()方法,可以看到其抛出Exception,这意味着重写该方法时,完全无须进行任何异常处理,是把异常直接抛给Struts2框架处理;Struts2框架爱接收到Action抛出的异常之后,将根据struts.xml文件配置的异常映射,转入指定的视图资源。

  为了使用Struts2的异常处理机制,必须打开Struts2的异常映射功能,开启异常映射功能需要一个拦截器,在struts-default.xml中已经定义好,因此我们直接使用即可。

6.2 声明式异常捕捉

  Struts2的异常处理机制是通过在struts.xml文件中配置<exception-mapping...>元素完成的配置该元素时,需要指定如下两个属性:

  • exception:此属性指定该异常映射所设置的异常类型
  • result:此属性指定Action出现该异常时,系统返回result属性值对应的逻辑视图名

当然也分为局部与全局:

  • 局部异常映射:将<exception-mapping...>元素作为<action...>元素的子元素配置
  • 全局异常映射:将<exception-mapping...>元素作为<global-exception-mappings>元素的子元素配置

注意:全局异常映射的result属性值通常不要使用局部结果,局部异常映射的result属性值既可以使用全局结果,也可以使用局部结果。

6.3 输出异常信息

  当Struts2框架控制系统进入异常处理页面后,还需要在对应页面中输出指定异常信息。我们可以使用Struts2标签进行输出:

  • <s:property value="exception"/>:输出异常对象本身(同样也可以采用<s:property value="exception.message"/>)
  • <s:property value="exceptionStack"/>:输出异常堆栈信息

七、Convention插件与“约定”支持

待完善——》》》》
 

参考资料:

  • 《轻量级JavaEE企业应用实战 第四版》

最后修改时间:2017年3月29日16:14:23

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值