??xml version="1.0" encoding="utf-8" standalone="yes"?>
本章我们会(x)讨论5U基?/span>Struts2代码的特征。从2002q以来,q些架构元素随着代码的发展仍然存?/span>--从开始的WebWorkQ经q?/span>WebWork分解?/span>WebWork2?/span>XWorkQ一直到最后{变ؓ(f)Struts2.
4?/span>1 概念的分?/span>
作ؓ(f)一?/span>web应用开发h员,有多U别的功能需要进行阐qͼ(x)
Q?/span>1Q在request/response周期期间Q需要对作ؓ(f)其核心目标的每个action的逻辑q行说明Q?/span>
Q?/span>2Q访问或拥有可以用来执行action逻辑或访问资源的业务对象Q?/span>
Q?/span>3Q编译、映和转换Q以便把HTML中基于字W的D{化ؓ(f)原始cdQ以?qing)把视图对象转换Z务对象或数据表来q行表示Q?/span>
Q?/span>4Qؓ(f)应用中分l的action或所有的action提供功能的横切关注点?/span>
?/span>Struts2架构中,每一个概念都是独立的。功能和逻辑不需要放|在专门?/span>action里。我们来看看上面所提到的概念以?qing)它们是如何处理的?x)
Q?/span>1Q每?/span>Action逻辑--q是最单的概念。每?/span>action负责它需要提供的逻辑或功能;
Q?/span>2Q编译、映和转换--3者是各自有所不同的概念,但是有一个相同点是它们都是作ؓ(f)核心action逻辑的补充。编译和cd转换由框架本w进行处理。来自于HTML的字W值在action开始处理之前被转换为基本类型或被注入到action?/span>--所有需要的都在此处。映由一个特定的拦截器进行处理。通过对一?/span>actionq行配置Q说明其所拥有的域模型Qƈ准确指明其在HTML中所对应的域Q框架将?x)?/span>UI映射到该域模型上。它甚至?x)生成对象图?/span>
Q?/span>3Q横切关注点--拦截器是提供横切功能的主要角艌Ӏ开发者可以实现拦截器Q然后把它们应用到所?/span>action、某个特定包中的所?/span>action或者选择某些指定?/span>action中去。另一个横切关注点是用h口层?/span>Struts2可以使用其所提供的称?#8220;主题Q?/span>themesQ?#8221;的标{来为此提供帮助。不同的主题可以被开发来提供不同的层选项Qƈ被应用到单个标签或整个应用(通过把它设ؓ(f)默认|(j)?/span>
4?/span>2 松耦合
WebWork的早期目标之一是提供一个松耦合的框架。其2.0版更加强调了q点Q把代码分成了两个项目,一个是XWork--提供普通的命o(h)模式(generic command-pattern)框架Q另一个是WebWork--?/span>XWork提供明确?/span>web接口。这?/span>WebWork架构基础上的变化创徏了二者的q关系。原来赖以成名的“WebWork”现在实际上是WebWork?/span>XWork的结合体?/span>
作ؓ(f)一个独立的目Q?/span>XWork现在可以被用于其它项目的一部分?/span>--实际上也实是这栗?/span>Swingwork是一个这L(fng)目--它是一个基?/span>Swing的、底层?/span>XWork?/span>MVC框架。另外一个例子就?/span>JMS前端--执行或共享与web UI相关?/span>XWork action。这些例子提供了高水qx耦合的典型案例?/span>Struts2是又一个采?/span>XWork的项目?/span>
松耦合思想已经更近一步,集成到整个框?/span>--从处?/span>action的第一步直到最后一步。实际上Q在Struts2里几乎没有什么不可配|的--个h认ؓ(f)q个特点既是Struts2发展的极大原动力之一Q但同时也是光大缺陷之一?/span>
松耦合配置的一般案例包括:(x)
Q?/span>1Q把URL映射?/span>actionQ?/span>
Q?/span>2Q把一?/span>action的不同结果映到提交的页面;
Q?/span>3Q把处理期间产生的异常映到提交的异帔R面;
不常使用?/span>Struts2特定的案例包括:(x)
Q?/span>1Q如果你不想使用Sping框架Q就得配|业务对象工厂;
Q?/span>2Q改?/span>URL映射C?/span>actioncȝ方式Q?/span>
Q?/span>3Qؓ(f)actionl果增加新的l果cdQ?/span>
Q?/span>4Qؓ(f)新的框架功能增加插gQ?/span>
Q?/span>5Q通过拦截器配|框架功能Q?/span>
松耦合pȝ的好处是众所周知?/span>--增强了可试性、易于扩展框架特征等。但有一个缺P׃可置配的灉|性(其是与拦截器相关的Q,D一个特?/span>action的处理\径不易被开发者所理解。这一点在q行调试的时候比较明显。由于不理解发生了什么事Q一个不了解pȝ的开发h员将不能快速高效的q行调试。问题本w可能比较简单,比如׃个没有进行正配|的拦截器引L(fng)Q或者甚臛_是因为拦截器的顺序不对引L(fng)。只要理解处理\径上的每一个细节,很快׃(x)扑ֈ问题的解军_法?/span>
4?/span>3 易测试性(TestabilityQ?/span>
在过dq中Q单元测试已l成Y件开发过E中事实上的标准。它不仅保证了类逻辑的一致性,而且通过在类开发过E中甚至开发之前实现单元测试,会(x)减少设计的复杂性,q且?x)设计更?f)健壮?/span>
Struts2的前w,WebWork是建立在这U环境之上的。由于框架元素的松散耦合Q测试很Ҏ(gu)q行。在web应用开发过E中Q?/span>action、拦截器、结果、对象工厂及(qing)其它被开发的lg能够独立地进行测试。由?/span>action和拦截器最为常用,我们对q两U组件进行详l的探讨?/span>
Actions
通常在框架中是通过执行“execute()”Ҏ(gu)来调?/span>action的,或者在配置时调用Q何一个返回gؓ(f)字符|StringQ的Ҏ(gu)来进行调用的。从易测性的角度来看Q这再简单不q了?/span>
我们来看一个例子。下面是一个数字篏加的actionc:(x)
public class MyAction {
private int number;
public int getnumber() { return number; }
public void setNumber( int n ) { number = n; }
public String execute() {
number += 10;
return “success”;
}
}
׃?/span>action?/span>POJOQ单元测试只需?/span>action实例化、调用方法,q在断点出得到预期的l果可以了。所有搜数据和资源都通过setterҎ(gu)提供l?/span>action。因?/span>action需要的M数据都能够直接在action里进行设|?/span>
在本例中我们需要两个断?/span>--一个ؓ(f)“execute”Ҏ(gu)的输行设|,另一个则是ؓ(f)验证action的状态是否是我们预期的。单元测试如下所C:(x)
public class myActionTest extends TestCase {
…
public void testExecute() {
MyAction action = new MyAction();
Action.setNumber(5);
assertEquals("success", action.execute());
assertEquals(15,action.getNumber());
}
}
对于资源E微有点ȝ(ch)Q类?/span>jMockq样的库可以被用来提供资源的模拟实现Q测试在action和资源之间的交互是否正确?/span>
管本例是用JUnit写成的,但是TestNG或其它Q何框枉可以使用?/span>
拦截?/span>(Interceptors)
当你~译拦截器的时候,试?x)稍微有点复杂。不q也有一些额外的帮助可以利用。用拦截器q行工作有两U情景?/span>
W一U情景是使用在调用时?/span>ActionInvocation对象q行交互的拦截器。在执行后,你能够通过在断点出得到拦截器本w的状态来验证逻辑的正性。在q种情景下,你能够利用与试action同样的方法来试拦截器。实例化拦截器、创Z?/span>ActionInvocation对象的模拟实?/span>(该对象带有在试拦截器时会(x)用到的?/span>)、调?/span>interceptҎ(gu)、在断点处得到预期的l果。这些可能发生在拦截器本w、被调用Ҏ(gu)的返回结果或是由pȝ抛出的异常?/span>
W二U情景是使用与其环境或拦截器堆栈中的其它拦截器发生交互的拦截器。在q种情况下,试需要通过ActionProxycMactionq行交互Q还会(x)需要访问拦截器本n不能讉K的其它环境对象?/span>
XWork库通过提供XWorkTestCasecL?/span>JUnit试提供帮助、通过提供TestNGStrutsTestCase ?/span>TestNGXWorkTestCasecL?/span>TestNG试提供帮助。这些ؓ(f)ConfigurationManager,Configuration, Container ?/span> ActionProxyFactorycȝ实例提供了测试实现。涉?qing)到的类q有XWorkTestCaseHelper ?/span> MockConfiguration.{?/span>
现在我们已经具备了安装环境的基础Q测试本w变得容易了--遵@在第一U情景里所描述的步骤即可。唯一的不同点在于Q不是调用拦截器?/span>intercept()Ҏ(gu)Q而是需要调?/span>ActionProxycȝexecute()Ҏ(gu)。如下述代码所C:(x)
ActionProxy proxy =
actionProxyFactory.createActionProxy(NAMESPACE,NAME,null);
assertEquals("success", proxy.execute());
在这U情况下Q测试将?x)设|断Ҏ(gu)获取预期?/span>actionl果倹{?/span>action值或值堆栈的倹{被执行?/span>action能够在执行前或执行后通过下面的调用进行访问:(x)
MyAction action=(MyAction)proxy.getInvocation().getAction();
而值堆栈则可以通过下面的调用进行访问:(x)
proxy.getInvocation().getStack();
4?/span>4 模块?/span>(Modularization)
随着应用pȝ来庞大,?/span>web应用分离多模块就变得来重要。模块化允许在一个项目中开发的功能或新的框架特征能够独立打包,q可以在其它目中进行重用?/span>Struts2已经把模块化作ؓ(f)其架构的基础部分Q允许开发者独立工作和~译彼此的项目?/span>
下面是一些对应用q行模块化的Ҏ(gu)Q?/span>
(1)配置信息能够被分为多个文?/span>--qƈ不媄(jing)响应用的打包Q由于配|信息根据功能界限进行逻辑分离而易于查找,从而简化的开发h员的工作?/span>
(2)独立的应用模块可以以插g的Ş式进行创?/span>--Z提供怸特定特征所需的一切都可以打包在一起ƈ作ؓ(f)插g独立发布。这包括action、拦截器、拦截器堆栈、视图模板(JSP除外Q等。浏览器插g的配|就是一个例子,该插件提供了一个完整的模块Q当你把该模块添加到你的应用中时Q其提供了一?/span>web接口来查看配|信息?/span>
(3)可以创徏新的框架功能插g-非特定应用的新功能可以以插g形式捆绑q在不同的应用中使用?/span>
从技术角度来_(d)所有这些对应用q行模块化的方式都是一L(fng)--都具有同L(fng)配置元素Q除了名UC同之外,“struts-plugin.xml”是系l自动加载插件时的配|文Ӟ(j)、具有相同的目录l构Qƈ且它们也包含同样的框架和应用元素?/span>
插g的两U类型之间的唯一不同是你在概念上如何看待它们,以及(qing)哪些元素和配|被攑օ分发包中?/span>
׃插g为核心框架功能提供了可选实玎ͼ所以有一些额外的配置元素。尽在“struts.xml”?#8220;struts-default.xml”配置文g中可以用这些元素,但是通常情况下它们还是在插g配置文g中进行配|?/span>
对于插g来说Q可选实现的配置分ؓ(f)两步Q?/span>
1、?/span><bean .../>标签中来提供可选接口的实现Q用一个唯一的键来识别它?/span>
2、?/span><constant … />标签在已配置的接口实C选择一个?/span>
我们来更详细地看一下这两个步骤?/span>
<bean .../>标签允许插g提供扩展点的实现信息。下面的例子是一个展C在"struts-default.xml"文g中ؓ(f)一个对象工厂进行配|的情况Q?/span>
<bean name="struts"
type="com.opensymphony.xwork2.ObjectFactory"
class="org.apache.struts2.impl.StrutsObjectFactory" />
属性提供了?/span>Struts2中创建和使用一个可选对象实现的所有信息。可以用的属性如下所C:(x)
class--提供cȝ全名Q?/span>
type--指明c需要实现的接口Q?/span>
name--Ҏ(gu)个类q行唯一识别的简Uͼ
static--指明是否把静态类Ҏ(gu)注入到类的实例;
scope--指明所用实例的范围Q其值可能ؓ(f)Q?/span>"default"?/span>"request"?/span>"session"?/span> "singleton"?/span>"thread"?/span>
optional--如果gؓ(f)“true”Q既是在创徏cd例时出现错误Q将仍然l箋加蝲?/span>
接下来,<constant … />标签允许开发h员选择使用哪个配置。只有两个属?/span>--一个属?#8220;name”提供了你的新实现所改变的扩展点的名Uͼ另一个属?#8220;value”是?/span><bean … />标签中配|的“name”名称?/span>
<constant name="struts.objectFactory" value="plexus" />
<constant … />标签是把一个新Dl一个已知属性的一U方式。该g可以?/span>"web.xml"配置文g中?#8220;init-param”q行修改Q或者在““struts.properties”配置文g中作Z个名值对Q?/span>name-value pairQ进行修攏V?/span>
如果你没在开发一个插Ӟ而只是用常?/span>"struts-xml"配置文g中的q些技巧,q里q有一个捷径。在<constant ... />标签里,使用你通常攑֜<bean ... />标签里的cd?/span>---q样做可以避免对<bean ... />标签的需求?/span>
下面q张表列Z可配|扩展点的接口和属性名Uͼ(x)Q表省略Q?/span>
惯例优先配置的原则是Rails带给L应用开发的一个概c(din)不是提供在很多应用中都很类似的配置文gQ而是假定在多数情况下开发h员将?x)遵从一个特定的模式。这个模式非常普遍以至于可以被认为是一个惯例,q样可以在框架中提供一个默认的配置而不是ؓ(f)每个新的应用提供一个配|。默认情况下Q开发h员不需要提供配|信息。但是如果有与惯例配|信息不同的需要,得提供相应的配|信息来代替默认配置信息?/span>
Struts2已经采用了这U理c(din)松耦合l?/span>Struts2提供大的Ҏ(gu),但是同时q也意味着该框架很难进行配|。?/span>惯例优先配置原则则ؓ(f)q两U对立的力量提供了^衡,使开发h员的开发可以更加简单和高效?/span>
?/span>Struts2中,惯例优先配置的例子包括:(x)
Q?Q?span style="font-family: "Times New Roman"; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">
隐式配置文g加蝲?/span>配置文g“Struts-default.xml”?#8220;struts-plugin.xml”是自动加载而不是显式加载的?/span>
Q?Q?span style="font-family: "Times New Roman"; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">
插g中的代码?/span>当利用插件中的代码时Q?/span>action名称和结果字W二者的l合来自动搜索结果模板,所以对?#8220;/user/add.action”来说Q将?x)?f)一?#8220;success”的结果返?#8220;/user/add-success.jsp”模板Q而ؓ(f)一?#8220;error”的结果返?#8220;/user/add-error.jsp”模板?/span>
Q?Q?span style="font-family: "Times New Roman"; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">
默认l果?qing)结果类?/span>?/span>当对actionq行配置Ӟ当用默认的“success”?/span>JSPӞ不必指明l果和类型?/span>
Q?Q?span style="font-family: "Times New Roman"; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">
l定Spring业务服务?/span>当安装了Spring框架插g后,不必为每?/span>action配置其所需的基?/span>Spring提供的业务,因ؓ(f)q些业务?x)自动?/span>actionq行l定?/span>
在前面的章节中,我们已经看到了几个默认配|,以及(qing)如何通过配置使用新值来代替默认倹{?/span>更多的配|选项,以及(qing)更多的惯例将?x)在接下来的章节中进行探讨?/span>
一个名?#8220;default.properties”的属性文件包含在“Struts2-Core”JAR文g中。你可以在你目源文件\径的根目录下创徏一个名?#8220;struts.properties”的文Ӟ来对某个属性进行修攏V这样你可以增加你惌修改的属性。新的属性值将?x)覆盖默认倹{?/p>
在开发环境下Q你可能?x)考虑修改下面一些属性:(x)
(1) struts.i18n.reload = true Q-允许国际化文仉载;
(2) struts.devMode = true Q-允许开发模式以提供更方便的调试功能Q?br> (3) struts.configuration.xml.reload = true Q-允许XML配置文g重蝲功能Q以便在有变化时可以重蝲而不需重蝲servlet容器中的整个web应用Q?br> (4) struts.url.http.port = 8080 Q-讄服务器允许端口,以便正确创徏生成的URL?/p>
二、struts.xml文g说明
struts.xml文g包含了当你开发action旉要修改的配置信息。下面我们来看一下该文g的固定结构?br> Ҏ(gu)你所开发应用的功能不同Q有可能在你的应用中完全用不着“struts.xml”配置文g。我们这里所说的配置能够被包含注释的替代Ҏ(gu)?#8220;web.xml”中的初始化参数及(qing)可替换URL映射配置q行处理。需?#8220;struts.xml”文gq行配置的属性有全局l果、异常处理及(qing)自定义拦截器堆栈?/p>
q是一个XML文gQ所以第一个元素是XML版本和解码信息。接下来是该XML的文档类型定义(DTDQ,提供该文档所包含元素的结构信息,以便被XML解析器和~辑器用?/p>