
这张图来自于Struts2的Reference,咱们可以在图中看到许多咱们不熟悉的名词,好比ActionProxy,Interceptor等等。这些都是Struts2的Control层的重要元素,也是Struts2的Control层的一个层次化的体现。
上面的这张图基本上可以归纳了Struts2的整个生命周期。接下来,咱们就对Action中的一些重要元素进行简单的描述。
1. Action映射:
action映射是Struts2框架中的基本” 工做单元”,action映射就是将一个请求URL(即action的名字)映射到一个action类,当一个请求匹配某个action的名字时,框架就使用这个映射来肯定如何处理请求。html
2. 使用method属性
在配置action时,咱们能够经过action元素的method属性来指定action调用的
方法,所指定的方法,必须遵循与execute方法相同的格式。
在Struts2.xml文件中,咱们能够为同一个action类配置不一样的别名,并使用
method属性。
在Struts.xml文件中为同一个Action类配置不一样的别名
<!-- 使用method属性 -->java
<struts>
<!--
<constant name="struts.enable.DynamicMethodInvocation" value="false" />设计模式
<include file="example.xml"/>安全
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">HelloWorld</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
-->
<!-- Add packages here -->
<constant name="struts.devMode" value="true" />
<package name="front" extends="struts-default" namespace="/">
<action name="index" class="com.bjsxt.struts2.front.action.IndexAction1">
<result name="success">/ActionIntroduction.jsp</result>
</action>
</package>app
</struts>框架
对应的MethodAction类,代码以下:jsp
package com.bjsxt.struts2.front.action;ide
public class IndexAction1 {
public String execute() {
return "success";
}
}函数
3.动态方法调用:post
另一种无需配置就能够直接调用Action中的非execute方法的方式,是使用
Struts2的动态方法调用。
动态方法调用是在action的名字中使用感叹号(!)来标识要调用的方法名,其语法格
式为 actionName!methodName.action
Action的介绍
多数的MVC框架中的Control层,都是一个Java对象。按照惯例,咱们一般会把这个层次上面的Java对象统称为Action层。本篇文章,咱们就来简单介绍一下Struts2中Action的相关内容。
传统的MVC框架中,Control层通常都是一个相似与Servlet的一个Java对象。由于从职责上讲,Control层须要完成如下的职责:
1. 接收从Web容器传递过来的参数,并作恰当的类型转化
2. 调用逻辑处理
3. 搜集数据,并返回到视图
而在这个其中的第一步和第三步,都离不开Web容器中的对象的处理。
Struts2中的Action,与其余传统的MVC框架不一样,使用了XWork的Action来构造Control层。让咱们首先来看看Action的接口定义:
咱们只须要实现这个接口,就能够在其中编写业务逻辑完成咱们的功能。
在这个接口定义中,咱们能够明显看到与传统的MVC框架之间的区别:Struts2中的Action,并不须要依赖于特定的Web容器。咱们看不到相似HttpServletRequest,HttpServletResponse等Web容器相关的对象。
而这一点,也带来了问题:
提问:Struts2的Action并不带有任何Web容器相关的对象,Action又是如何工做在Web容器中的呢?
虽然Struts2的Action只是一个很是普通的Java对象,并不具有任何Web容器的特质,可是咱们须要把Action放到一个更加大的环境中来看。事实上,Struts2为Action的执行,准备了完整的数据环境和执行环境。而这个执行环境,就保证了Action在Web容器中的顺利运行。
在Struts2中,每一个Http的请求,会被发送到一个Filter。而这个Filter,就会针对每一个请求,建立出一个代码的执行环境,并在这个基础上,为每一个执行环境配备与之对应的数据环境,这个数据环境中的内容,就来自于Web容器中的一个又一个对象。这样,就可以顺利调用Action执行代码而无需担忧它是否运行在Web容器中了。
至于这个执行环境和数据环境究竟是什么,咱们接下来会详细讲到。
提问:Struts2的Action并不带有任何Web容器相关的对象,Action中又如何与Web容器进行通讯并获取Web容器的相关对象呢?
刚刚咱们提到Struts2会为每一个Http的请求创建一个执行环境和数据环境。其中,数据环境就成为了Action获取Web容器的基础。因此,当Action须要获取Web容器的相关对象,须要经过数据环境来进行。
Struts2的Action的这一个重要特性,至少能为咱们带来如下好处:
1. 使得Struts2的Action很是易于测试
若是咱们彻底不考虑Action的执行环境,仅仅把Action看做一个普通的Java对象,那么咱们甚至能够直接new一个Action的对象,经过执行其中的方法完成测试。这样,咱们就不须要任何的Mock,来模拟Web容器的环境。
2. 结合Action的执行环境,使得Struts2在Control这个层次上,可以定义更加丰富的执行层次
由于Action是一个普通的Java类,而不是一个Servlet类,彻底脱离于Web容器,因此咱们就可以更加方便地对Control层进行合理的层次设计,从而抽象出许多公共的逻辑,并将这些逻辑脱离出Action对象自己。事实上,Struts2也正是这么作的,不管是Interceptor,仍是Result,其实都是抽象出了Action中公共的逻辑部分,将他们放到了Action的外面,从而更加简化了Action的开发。
3. 使得Struts2的Action看上去更像一个POJO,从而更加易于管理
Struts2的Action是一个线程安全的对象。而Web容器传递过来的参数,也会传递到Action中的成员变量中。这样,Action看上去就更像一个POJO,从而可以方便的被许多对象容器进行管理。好比说,你能够很是方便得把Action归入到Spring的容器中进行管理。
在大概了解了Struts2的Action后,咱们来重点研究一下在Struts2的Action周围,为Action进行服务的一些重要元素,这些元素将涵盖Action的数据环境,Action的执行环境、Action的调度者、Action的层次结构和Action的执行结果。
ActionContext —— 数据环境
以前咱们提到了Struts2的Action并非一个Servlet,它是脱离了Web容器的。可是对于一个Web框架来讲,全部的数据请求(Request)和数据返回(Response)都来源于Web容器,那么Action在执行的时候,如何去获取这些数据呢?
这个问题的答案就在于,咱们须要为每一个Action准备一个数据环境,这个数据环境被称之为:ActionContext。因为Action是应对于一个又一个的URL请求,因此ActionContext应该具有如下的特性:
1. ActionContext应成为Action与Web容器之间的桥梁
2. ActionContext中应该保存有针对某个请求的详细信息
3. ActionContext应该是一个线程安全的类对象
Interceptor —— 丰富的层次结构
简单回顾一下上面所提到的Action的职责,咱们看到,须要在Action这个层面上完成的事情还很多。而完成这些职责,就须要咱们对这些职责进行合理的分类和排序,将他们组织成有序的执行队列。在Struts2中,使用了一种相似责任链的设计模式对这些不一样的职责进行分类并串联起来,从而使得Action层具有了丰富的层次结构。而在这个执行队列中的每一个元素,就被咱们称之为Interceptor,也就是拦截器。
拦截器是AOP中的概念,它自己是一段代码,能够经过定义“织入点”,来指定拦截器的代码在“织入点”的先后执行,从而起到拦截的做用。正如上面Struts2的Reference中讲述的,Struts2的Interceptor,其拦截的对象是Action代码,能够定义在Action代码以前或者以后执行拦截器的代码。
若是仔细留意一下Action LifeCycle图中的Interceptor和Action的部分,咱们能够看到,Interceptor一层一层的把Action包了起来。这是一个典型的堆栈结构,在代码执行的时候,每一个Interceptor不只须要文成它自身的逻辑,还经过递归调用负责下一个拦截器或Action的调用。
也正如Struts2的Reference所说,Struts2提供的绝大多数的功能支持,都经过Interceptor来实现,这些Interceptor能够随意进行配置,而且可以方便的插入到程序中去运行。
Result —— 执行结果
有执行就必然有执行的结果。在Struts2中,Action的执行结果被抽象成了一个层次。在这个层次中,能够定义任意类型的View层的结构。也就是说,Struts2并不强制View层的表现形式,能够是JSP、Freemarker模板、二进制流输出等等。
Struts2把执行结果抽象成一个层次,使得你能够再也不关注许多视图整合上面的细节,只须要考虑视图的类型和数据的准备,这样,你也能够没必要在沉浸在杂乱的构造视图的代码中。
ActionProxy —— 执行环境
有了拦截器Interceptor,有了Action自己,也有了Action的执行结果Result,咱们就须要一个相似调度器的产品,将这些元素整合起来,进行调度执行。在上面的Action Lifecyle的图中,咱们能够看到,Interceptor、Action和Result都处于ActionProxy中,因此ActionProxy就成为了全部这些元素的执行环境。
既然是执行环境,那么ActionProxy就须要提供Action执行的时候一切所须要的配置、参数等等,固然,也要有进行Action调用的入口。因此让咱们来看一下ActionProxy的接口:
很显然,在这其中,prepare和execute方法是用做Action调用的入口函数,其余的接口定义都与Action执行时的运行参数和配置有关。
ActionInvocation —— 调度者
在上面的ActionProxy的接口定义中,咱们能够看到有一个比较特殊的变量:ActionInvocation比较吸引咱们的眼球。从字面上去理解,ActionInvocation就是Action的调用者。事实上也是如此,ActionInvocation在这个Action的执行过程当中,负责Interceptor、Action和Result等一系列元素的调度。在以后的章节中,这个ActionInvocation类也将成为咱们解读Struts2源码的一个重要入手点。这个类将告诉你,Struts2是如何经过ActionInvocation来实现对Interceptor、Action和Result的合理调度的。