struts2框架--------Action和Result

时间:2021-07-06 19:38:37
Action概述
在MVC框架中,Controller层一般都是一个类似Servlet的一个Java对象。因为从职责上讲,Controller层需要完成以下的职责:
 1. 接收从客户端传递过来的参数,并做恰当的类型转化 ,可能会对请求进行服务器端验证

2. 调用逻辑处理

3. 搜集数据,并返回到视图

按照惯例,这个层次上面的Java对象统称为Action
Struts2中的Action,并不需要依赖于特定的Web容器。我们看不到类似HttpServletRequest,HttpServletResponse等Web容器相关的对象。Struts中的action, 默认就是一个POJO


在Struts2中,每个Http的请求,会被统一发送到一个核心Filter(StrutsPrepareAndExecuteFilter)
这个Filter,就会针对每个请求,创建出一个代码的执行环境,并在这个基础上,为每个执行环境配备与之对应的数据环境
这个数据环境中的内容,就来自于Web容器中的一个又一个对象(Interceptor)。这样,既能顺利调用Action执 行代码而无需担心它是否运行在Web容器中又不会把容器相关对象侵入action中,提高复用性并且便于单元测试

Struts2 action设计的优点
如果我们完全不考虑Action的执行环境,仅仅把Action看作一个普通的Java对象,那么我们甚至可以直接new一个Action的对象,通过执行其中的方法完成测试。这样,我们就不需要模拟Web容器的环境来完成单元测试。
因为Action是一个普通的Java类,而不是一个Servlet类,完全脱离于Web容器,所以我们就能够更加方便地对Controller层进行 合理的层次设计,从而抽象出许多公共的逻辑,并将这些逻辑从Action对象本身分离出来(外部化)
在struts2中,无论是 Interceptor,还是Result,其实都是抽象出了Action中公共的逻辑部分,将他们放到了Action的外面,从而更加简化了 Action的开发。
Struts2的Action是一个线程安全的对象
Web容器传递过来的参数,也会传递到Action中的同名成员变量中。这样,Action看 上去就更像一个POJO,从而能够方便的被许多对象容器进行管理。比如说,你可以非常方便得把Action纳入到Spring的容器中进行管理。 

Action的五大要素
Struts2框架会为Action提供的一些重要元素,这些元素 将涵盖Action的数据环境,Action的执行环境、Action的调度者、Action的层次结构和Action的执行结果。 

ActionContext —— 数据环境
Struts2的Action并不是一个Servlet,它是脱离了Web容器的。但是对于一个Web框架来说,所有的数据请求(Request)和数据返回(Response)都来源于Web容器,那么Action在执行的时候,如何去获取这些数据呢?
struts2框架为每个Action准备一个数据环境,这个数据环境被称之为:ActionContext。ActionContext就是Action与Web容器之间的桥梁,这个对象保存有针对某个请求的详细信息

Interceptor —— 丰富的层次结构
大部分请求响应处理职责都在Action这个层面上完成。要完成这些职责,就需要我们对这些职责进行合理的分类和排序,将他们组织成有序的执行队列。
在Struts2中,使用了一种类似职责链的设计模式对这些不同的职责进行分类并串联起来,从而使得 Action层具备了丰富的层次结构又不会过于臃肿(单一职责原则)。
在这个执行队列中的每个元素,就被我们称之为Interceptor,也就是拦截器。
拦截器是一 个典型的栈结构,在代码执行的时候,每个Interceptor不仅需要执行它自身的逻辑,还通过递归调用负责下一个拦截器或Action的调用。 


Result —— 执行结果
有执行就必然有执行的结果。在Struts2中,Action的执行结果也被抽象成了一个层次。在这个层次中,可以定义任意类型的View层的结构。

Struts2把执行结果抽象成一个层次,使得开发人员可以不再关注许多视图整合上面的细节,只需要考虑视图的类型和数据的准备,这样,开发人员就不必在沉浸在杂乱的构造视图的代码中。 


ActionProxy —— 执行环境
有了拦截器Interceptor,有了Action本身,也有了Action的执行结果Result,就需要一个类似调度器的产品,将这些 元素整合起来,进行调度执行
Interceptor、Action和Result都处于ActionProxy中,所以 ActionProxy就成为了所有这些元素的执行环境。


ActionInvocation —— 调度者
ActionInvocation就是Action的实际调用者。在这个Action的执行过程中, ActionInvocation负责Interceptor、Action和Result等一系列元素的调度


•Action执行过程

struts2框架--------Action和Result



struts.xml概述
在<struts>标签中可以有多个<package>,每一个<package>中可以有多个<action>. 每一个<package>实际上指定了一个Servlet访问路径(不包括动作名),如“/mystruts”。
extends属性指定该配置文件继承的另一个package。通常每个包都应该继承struts-default包,因为Struts2很多核心的功能都是拦截器来实现。struts-default定义了这些拦截器和Result类型。可以这么说:当包继承了struts-default才能使用struts2提供的核心功能。struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义。struts-default.xml也是Struts2默认配置文件。 Struts2每次都会自动加载 struts-default.xml文件。

<action>标签中的name属性表示动作名,class表示Action类全路径名。
<result>标签的name实际上就是execute方法返回的字符串,如果返回的是“positive”,就跳转到positive.jsp页面,如果是“negative”,就跳转到negative.jsp页面。

Action名称的搜索顺序

1.获得请求路径的URI,例如url是:http://server/struts2/path1/path2/path3/test.action

2.首先寻找namespace/path1/path2/path3package,如果不存在这个package则执行步骤3;如果存在这个package,则在这

package中寻找名字为testaction,当在该package下寻找不到action时就会直接跑到默认namaspacepackage里面去寻找

action(默认的命名空间为空字符串“” ),如果在默认namaspacepackage里面还寻找不到该action,页面提示找不到action

3.寻找namespace/path1/path2package,如果不存在这个package,则转至步骤4;如果存在这个package,则在这

package中寻找名字为testaction,当在该package中寻找不到action时就会直接跑到默认namaspacepackage里面去找名字

testaction,在默认namaspacepackage里面还寻找不到该action,页面提示找不到action

4.寻找namespace/path1package,如果不存在这个package则执行步骤5;如果存在这个package,则在这个package中寻找

名字为testaction,当在该package中寻找不到action时就会直接跑到默认namaspacepackage里面去找名字为testaction,在

默认namaspacepackage里面还寻找不到该action,页面提示找不到action

5.寻找namespace/package,如果存在这个package,则在这个package中寻找名字为testaction,当在package中寻找不

action或者不存在这个package时,都会去默认namaspacepackage里面寻找action,如果还是找不到,页面提示找不到action


Action配置中的各项默认值

<packagename="itcast"namespace="/test"extends="struts-default">

        <action name="helloworld"class="cn.itcast.action.HelloWorldAction"method="execute" >

  <resultname="success">/WEB-INF/page/hello.jsp</result>

        </action>

  </package>

1>如果没有为action指定class,默认是ActionSupport

2>如果没有为action指定method,默认执行action中的execute()方法。

3>如果没有指定resultname属性,默认值为success

Action辅助类--ActionSupport

理论上Struts 2.0 的Action 无须实现任何接口或继承任何类型,但是,为了简化Action开发,利

用struts框架提供的一些辅助功能(数据验证、国际化等)大多数情况下都会继承

com.opensymphony.xwork2.ActionSupport 类,并重写(Override)此类里的String execute()

方法

Action辅助类—ActionSupport(2)
ActionSupport基类中定义了五个标准的返回值,当然我们可以自己随意定义返回的名字

String SUCCESS ="success"; //默认是 SUCCESS 类型

String NONE ="none";

String ERROR ="error";

String INPUT ="input";

String LOGIN ="login";

ActionSupport常用方法:

getText(String aTextName);//国际化用到

...//getText(String aTextName)的重载方法

addActionMessage(String aMessage);

addFieldError(String fieldName, String errorMessage);

//校验失败后返回给客户端的信息,struts2标签<s:fielderror key=/>可以取得

addActionError(String anErrorMessage);



Action执行结果处理-Result

Action处理完请求后,将返回一个字符串,该字符串实际上定义了一个视图(view)资源的逻辑名

Struts2通过配置文件将该视图的逻辑名与物理视图做了映射。这种映射关系在struts.xml通过result节点进行配置

Struts2支持多种类型结果映射,不同的类型由result元素中的type属性指定

Result分为局部result和全局result

Action中result的常用转发类型
Chain(chain) 构成一条动作链
Dispatcher(dispatcher) 默认类型,转发,相当forward
Redirect(redirect) 重定向到另一个URL。相当于sendRedirect
RedirectAction(redirectAction) 重定向到另一Action
Stream(stream) 把一个InputStream流发送给浏览器(下载用)
PlainText(plaintext) 发送普通文本,通常用来显示JSP页面的源代码
Json(json)

Action之间跳转
Struts2中action间的跳转分为两种情况,即同一个package中不同action间跳转和不同package中action间跳转。

不管是不是在同一个包中,首先要明确的是要实现跳转,必须要将result中type属性设为chain(forward)或redirectAction(redirect)。 

同一个包中action间的跳转
1. 服务器端跳转:result type设为chain,跳转的Action的result内容直接写要跳转到的Action的name即可。 比如:

<action name="r1"class="R1">    

         <result name="success"type="chain">r2</result>    

   </action>    

   <action name="r2" class="R2">    

         <result name="success"type="dispatcher">/1.jsp</result>    

   </action>

2. 客户端跳转:result type属性设为redirectAction,其他同服务器端跳转


不同包下action之间跳转
需要在result节点中添加param子节点,指明跳转到action所在包的namespace
同样存在客户端跳转(redirect)和服务器端跳转(chain)两种方式,比如:

<result type="redirectAction">

  <param name="actionName">helloworld</param>

  <paramname="namespace">/test</param>

  <paramname="method">method123</param>

</result>

多个Action共享一个视图--全局result配置

多个action中都使用到了相同视图,这时我们应该把result定义为全局视图。struts1中提供了全局forward,struts2中也提供了相似功能:

<package....>

  <global-results>

  <result name="message">/message.jsp</result>

  </global-results>

</package>


Action的属性注入值(配置参数)

Struts2Action中的属性提供了依赖注入(DI)功能,在struts2的配置文件中,我们可以很方便地为Action中的属

性注入值。注意:属性必须提供setter方法。

public class HelloWorldAction{

  private String savePath;

  public String getSavePath() {

  return savePath;

  }

  public void setSavePath(StringsavePath) {

  this.savePath =savePath;

  }

       ......

}

<packagename="itcast"namespace="/test" extends="struts-default">

  <action name="helloworld"class="cn.itcast.action.HelloWorldAction" >

  <param name="savePath">/images</param>

  <resultname="success">/WEB-INF/page/hello.jsp</result>

  </action>

</package>

上面通过<param>节点为actionsavePath属性注入“/images”


配置传参和客户端传参
如果客户端传过来参数,该参数生效,配置参数无效
。。。。没有传参,struts2容器会自动读取action下param节点中的参数,为同名属性赋值

Struts 2处理的请求后缀

面我们都是默认使用.action后缀访问Action。其实默认后缀是可以通过常量struts.action.extension进行修改的,例如:我们可以配

Struts 2只处理以.do为后缀的请求路径:

<?xmlversion="1.0" encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC

    "-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"

   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

   <constant name="struts.action.extension" value="do"/>

</struts>

如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。如:

 <constant name="struts.action.extension" value="do,go"/>


细说常量定义

量可以在struts.xmlstruts.properties中配置,建议在struts.xml中配置,两种配置方式如下:

struts.xml文件中配置常量

<struts>

   <constant name="struts.action.extension" value="do"/>

</struts>

struts.properties中配置常量

struts.action.extension=do

因为常量可以在下面多个配置文件中进行定义,所以我们需要了解struts2加载常量的搜索顺序:

struts-default.xml

struts-plugin.xml

struts.xml

struts.properties

web.xml

如果在多个文件中配置了同一个常量则后一个文件中配置的常量值会覆盖前面文件中配置的常量值.


常用的常量介绍

<!-- 指定默认编码集,作用于HttpServletRequestsetCharacterEncoding方法 和freemarker 、velocity的输出 -->

    <constantname="struts.i18n.encoding" value="UTF-8"/>

    <!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理

struts.action.extension

    如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。-->

    <constant name="struts.action.extension"value="do"/>

    <!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭-->

    <constant name="struts.serve.static.browserCache"value="false"/>

    <!-- struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开-->

    <constant name="struts.configuration.xml.reload"value="true"/>

    <!-- 开发模式下使用,这样可以打印出更详细的错误信息-->

    <constant name="struts.devMode"value="true" />

     <!-- 默认的视图主题-->

    <constant name="struts.ui.theme"value="simple" />

    <!– spring集成时,指定由spring负责action对象的创建-->

    <constant name="struts.objectFactory"value="spring" />

 <!–该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false-->

<constantname="struts.enable.DynamicMethodInvocation"value="false"/>

 <!--上传文件的大小限制-->

<constantname="struts.multipart.maxSize"value=“10701096"/>


Struts2的处理流程
struts2框架--------Action和Result


为应用指定多个struts配置文件

大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿。为了避

struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后

struts.xml文件中包含其他配置文件。下面的struts.xml通过<include>元素指定多个配置文件:

<?xmlversion="1.0" encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC

    "-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"

   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

  <include file="struts-user.xml"/>

  <include file="struts-order.xml"/>

</struts>

通过这种方式,我们就可以将Struts 2Action按模块添加在多个配置文件中。

注意:用<include>引用的xml文件也必须是完整的struts2的配置。实际上<include>在引用时是单独解析的xml文件,而不是将被引用的文件插入到struts.xml文件中

 
为同一个action类创建多个action别名
在默认情况下,Struts2会调用动作类的execute方法。
但有些时候,我们需要在一个动作类中处理不同的动作。也就是用户请求不同的动作时,执行动作类中的不同的方法。
为了达到这个目的,可以在<action>标签中通过method方法指定要执行的动作类的方法名,并且在配置文件中建立多个action节点,比如:

<actionname="test" class="action.MyAction">

</action>

<actionname="my" class="action. MyAction" method="my">

</action>

动态方法调用

Action中存在多个方法时,我们可以使用!+方法名调用指定方法。如下:

public class HelloWorldAction{

  private String message;

  ....

  public String execute() throws Exception{

  this.message = "我的第一个struts2应用";

  return "success";

  }

 

  public String other() throws Exception{

  this.message = "第二个方法";

  return "success";

  }

}

假设访问上面actionURL路径为: /struts/test/helloworld.action

要访问actionother()方法,我们可以这样调用:

/struts/test/helloworld!other.action

如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用。

<constantname="struts.enable.DynamicMethodInvocation"value="false"/>


使用通配符定义action

<packagename="itcast"namespace="/test" extends="struts-default">

  <action name="helloworld_*"class="cn.itcast.action.HelloWorldAction"method="{1}">

  <resultname="success">/WEB-INF/page/hello.jsp</result>

  </action>

</package>

public class HelloWorldAction{

  private String message;

  ....

  public String execute() throws Exception{

  this.message = "我的第一个struts2应用";

  return "success";

  }

 

  public String other() throws Exception{

  this.message = "第二个方法";

  return "success";

  }

}

要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action

Struts2中获取HTTP相关对象
开发Web 应用程序当然免不了跟request,response,session等HTTP相关对象打交道
在struts1.xAction类的execute方法中,有四个参数,其中两个就是response和request。而在Struts2 中,action基本上就是个pojo,相应方法没有任何参数, 因此,就不能简单地从execute 方法(或其他自定义方法)中获得HTTP相关对象
在struts2中,有3种方法可以获取HTTP相关对象。其中前两种属于非IOC方式,后面一种属于IOC方式(DI)

ActionContext类
可以通过org.apache.struts2.ActionContext类的静态方法getContext()获取当前Action的上下文对象(实例化),再调用它的get方法获得相应的对象。代码如下:
HttpServletResponseresponse(HttpServletResponse) =
ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
HttpServletRequestrequest(HttpServletRequest) =
ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);

ServletActionContext类
该类是ActionContext类的子类,作为一个辅助类,提供了相应方法让你更快捷获取HTTP相关对象
HttpServletRequestrequest = ServletActionContext.getRequest();
  ServletContext servletContext =ServletActionContext.getServletContext();
  request.getSession()  
  HttpServletResponse response =ServletActionContext.getResponse();

使用Aware拦截器,由struts框架在运行时注入(IOC方式)
这种方法需要Action类实现相应的拦截器接口。如我们要获得HttpServletRequest对象,需要实现 org.apache.struts2.interceptor.ServletRequestAware接口
如果一个动作类实现了ServletRequestAware接口,在调用execute方法之前,struts2就会先调用Action的setxxxRequest方法,并将容器产生的request对象作为依赖传入这个方法。如果想获得HttpServletResponse、Cookie等对象,动作类可以分别实现ServletResponseAware 、CookiesAware 等接口。
这些接口都在org.apache.struts2.interceptor包中。