Struts 2的体系与Struts 1体系的差别非常大,因为Struts 2使用了WebWork的设计核心,而不是使用Struts 1的设计核心。Struts 2大量使用拦截器来处理用户请求,从而允许用户的业务逻辑控制器与Servlet API分离。
从数据流图上来看,Struts 2与WebWork相差不大,Struts 2同样使用拦截器作为处理(Advice),以用户的业务逻辑控制器为目标,创建一个控制器代理。
控制器代理负责处理用户请求,处理用户请求时回调业务控制器的execute方法,该方法的返回值将决定了Struts 2将怎样的视图资源呈现给用户。
图1.9显示了Struts 2的体系概图。

图1.9 Struts 2的体系概图
Struts 2框架的大致处理流程如下:
浏览器发送请求,例如请求/mypage.action、/reports/myreport.pdf等。
核心控制器FilterDispatcher根据请求决定调用合适的Action。
WebWork的拦截器链自动对请求应用通用功能,例如workflow、validation或文件上传等功能。
回调Action的execute方法,该execute方法先获取用户请求参数,然后执行某种数据库操作,既可以是将数据保存到数据库,也可以从数据库中检索信息。实际上,因为Action只是一个控制器,它会调用业务逻辑组件来处理用户的请求。
Action的execute方法处理结果信息将被输出到浏览器中,可以是HTML页面、图像,也可以是PDF文档或者其他文档。此时支持的视图技术非常多,既支持JSP,也支持Velocity、FreeMarker等模板技术。
当Struts 2创建系统的Action代理时,需要使用Struts 2的配置文件。
Struts 2的配置文件有两份:
— 配置Action的struts.xml文件。
— 配置Struts 2全局属性的struts.properties文件。
struts.xml文件内定义了Struts 2的系列Action,定义Action时,指定该Action的实现类,并定义该Action处理结果与视图资源之间的映射关系。
下面是struts.xml配置文件的示例:
<struts>
<!-- Struts 2的Action都必须配置在package里 -->
<package name="default" extends="struts-default">
<!-- 定义一个Logon的Action,实现类为lee.Logon -->
<action name="Logon" class="lee.Logon">
<!-- 配置Action返回input时转入/pages/Logon.jsp页面 -->
<result name="input">/pages/Logon.jsp</result>
<!-- 配置Action返回cancel时重定向到Welcome的Action-->
<result name="cancel" type="redirect-action">Welcome</result>
<!-- 配置Action返回success时重定向到MainMenu的Action -->
<result type="redirect-action">MainMenu</result>
<!-- 配置Action返回expired时进入ChangePassword的Action链 -->
<result name="expired" type="chain">ChangePassword</result>
</action>
<!-- 定义Logoff的Action,实现类为lee.Logoff -->
<action name="Logoff" class=" lee.Logoff">
<!-- 配置Action返回success时重定向到MainMenu的Action -->
<result type="redirect-action">Welcome</result>
</action>
</package>
</struts>
在上面的struts.xml文件中,定义了两个Action。定义Action时,不仅定义了Action的实现类,而且的定义Action的处理结果时,指定了多个result,result元素指定execute方法返回值和视图资源之间的映射关系。对于如下配置片段:
<result name="cancel" type="redirect-action">Welcome</result>
表示当execute方法返回cancel的字符串时,跳转到Welcome的Action。定义result元素时,可以指定两个属性:type和name。其中name指定了execute方法返回的字符串,而type指定转向的资源类型,此处转向的资源可以是JSP,也可以是FreeMarker等,甚至是另一个Action——这也是Struts 2可以支持多种视图技术的原因。
除此之外,Struts 2还有一个配置Struts 2全局属性的Properties文件:struts.properties。该文件的示例如下:
#指定Struts 2处于开发状态
struts.devMode = false
//指定当Struts 2配置文件改变后,Web框架是否重新加载Struts 2配置文件
struts.configuration.xml.reload=true
正如上面见到的,struts.properties文件的形式是系列的key、value对,它指定了Struts 2应用的全局属性。
Struts 2的标签库也是Struts 2的重要组成部分,Struts 2的标签库提供了非常丰富的功能,这些标签库不仅提供了表现层数据处理,而且提供了基本的流程控制功能,还提供了国际化、Ajax支持等功能。
通过使用Struts 2的标签,开发者可以最大限度地减少页面代码的书写。
看下面的JSP页面的表单定义片段:
<!-- 定义一个Action -->
<form method="post" action="basicvalid.action">
<!-- 下面定义三个表单域 -->
名字:<input type="text" name="name"/><br>
年纪:<input type="text" name="age"/><br>
喜欢的颜色:<input type="text" name="favorite"/><br>
<!-- 定义一个输出按钮 -->
<input type="submit" value="提交"/>
</form>
上面页面使用了传统的HTML标签定义表单元素,还不具备输出校验信息的功能,但如果换成如下使用Struts 2标签的定义方式:
<!-- 使用Struts 2标签定义一个表单 -->
<s:form method="post" action="basicvalid.action">
<!-- 下面使用Struts 2标签定义三个表单域 -->
<s:textfield label="名字" name="name"/>
<s:textfield label="年纪" name="age"/>
<s:textfield label="喜欢的颜色" name="answer"/>
<!-- 定义一个提交按钮 -->
<s:submit/>
</s:form>
则页面代码更加简洁,而且有更简单的错误输出。图1.10是上面使用Struts 2标签执行数据校验后的输出。

图1.10 使用Struts 2标签的效果
提示 Struts 2的标签库的功能非常复杂,该标签库几乎可以完全替代JSTL的标签库。而且Struts 2的标签支持表达式语言,这种表达式语言支持一个强大和灵活的表达式语言:OGNL(Object Graph Notation Language),因此功能非常强大。
Struts 2的控制器组件是Struts 2框架的核心,事实上,所有MVC框架都是以控制器组件为核心的。正如前面提到的,Struts 2的控制器由两个部分组成:FilterDispatcher和业务控制器Action。
实际上,Struts 2应用中起作用的业务控制器不是用户定义的Action,而是系统生成的Action代理,但该Action代理以用户定义的Action为目标。
下面是Struts 2的Action代码示例:
public class LoginAction
{
//封装用户请求参数的username属性
private String username;
//封装用户请求参数的password属性
private String password;
//username属性的getter方法
public String getUsername()
{
return username;
}
//username属性的setter方法
public void setUsername(String username)
{
this.username = username;
}
//password属性的getter方法
public String getPassword()
{
return password;
}
//password属性的setter方法
public void setPassword(String password)
{
this.password = password;
}
//处理用户请求的execute方法
public String execute() throws Exception
{
//如果用户名为scott,密码为tiger,则登录成功
if (getUsername().equals("scott")
&& getPassword().equals("tiger") )
{
return "success";
}
else
{
return "error";
}
}
}
通过查看上面的Action代码,发现该Action比WebWork中的Action更彻底,该Action无需实现任何父接口,无需继承任何Struts 2基类,该Action类完全是一个POJO(普通、传统的Java对象),因此具有很好的复用性。
归纳起来,该Action类有如下优势:
— Action类完全是一个POJO,因此具有很好的代码复用性。
— Action类无需与Servlet API耦合,因此进行单元测试非常简单。
— Action类的execute方法仅返回一个字符串作为处理结果,该处理结果可映射到任何的视图,甚至是另一个Action。