5.4 struts.xml的配置
Struts2绝大多数的配置都是在struts.xml中完成的,学习struts.xml文件是学习使用Struts2的基础。本节将详细讲述如何在struts.xml中定义和配置各种元素。
5.4.1 Action配置
Action是Struts2的基础“工作单元”。配置一个基本的Action只需要两个信息:Action名字和对应的Action类,这两部分就建立了一个最简单的Action配置。属性“method”用来告诉Struts2调用Action的哪个方法。在Action处理之后一般需要展示处理结果给用户,所以还需要把Action和Result映射在一起,如实例5-4所示。
【实例5-4】Action配置:struts.xml
01 <!--Struts action配置--> 02 <action name="helloWorld" class="example.HelloWorld" method="doWork"> 03 <result name="failure" path="Error.jsp" /> 04 <result name="ok" path="HelloWorld.jsp" /> 05 </action>
Action流程示意图如图5.3所示。
图5.3 Action流程示意图
【代码剖析】对于action的配置有几种默认的配置情况:
1)如果class没有定义,会默认使用com.opensymphony.xwork.ActionSupport类,它有一个execute()方法,默认返回“success”。
2)如果method属性为空,Struts2默认调用action类的“execute()”方法。如果action类中既没有execute()方法也没有在struts.xml文件中指定其他方法,Struts2会抛出异常。
3)如果第一个result的属性省略了,Struts2默认会把它当做“success”。
可以在表单或URL中用“actionName!something”的方式直接告诉Struts2调用Action类中的“something”方法。例如“formTest!delete.action”会调用FormAction类中的“delete”方法。这个方法必须是public且没有参数。
也可以指定一个当在struts.xml中找不到指定的action时执行的默认action。这一特性主要是用来满足为创建非常简单或相似的action类或元素的需求。默认action名可以在package元素里面这样配置,如实例5-5所示。
【实例5-5】默认Action配置:struts.xml
<!--定义一个包--> <package name="myPackage"> ...... <default-action-ref name="simpleViewResultAction"> <action name="simpleViewResultAction" class="SimpleViewResultAction"> <result type="velocity">${successUrl}</result> <result name="error" type="velocity">${errorUrl}</result> <result name="input" type="velocity">${inputUrl}</result> </action> ...... </package>
【代码剖析】上述代码配置了一个默认action。
5.4.2 Result配置
Struts2定义了一些默认Result:error、input、login、none和success。开发者当然也可以根据应用情况自由地定义结果。结果以“名字-值”的形式映射到结果类型。<result>标签告诉Struts2在Action被调用以后下一步做什么。Result在struts.xml文件中定义,嵌套在<action>标签里。如果location参数是唯一的参数,可以这样简单地定义:
<!--在result中添加参数--> <action name="bar" class="myPackage.barAction"> <result name="success" type="dispatcher"> <param name="location">foo.jsp</param> </result> </action>
如果<result>标签中没有<param>标签,Struts2就把<result> </result>里面的文字作为location。所以上面例子可以简化为:
<!--在result中添加参数--> <action name="bar" class="myPackage.barAction"> <result name="success" type="dispatcher">foo.jsp</result> </action>
如果没有指定<result>标签的type属性,Struts2默认为dispatcher类型(类似于Servlet标准中的ServletDispatcher的forward)。上面的例子可以简化为:
<!--在result中添加参数--> <action name="bar" class="myPackage.barAction"> <result>foo.jsp</result> </action>
使用全局Result映射是减少struts.xml配置内容的另外一种好方法。在Web应用程序中,很多Action通常都会使用一组通用的Result,可以把这些通用的Result定义集中起来,而不需要在每一个Action映射中逐一重复定义这些Result,这种方法又称为全局Result。实例5-6所示说明全局Result是如何定义的。
【实例5-6】全局result配置:struts.xml
01 <package name="default"> 02 <!--全局result配置可以在任何一个包内使用 --> 03 <global-results> 04 <result name="login" type="dispatcher"> 05 <param name="location">login.jsp</param> 06 </result> 07 </global-results> 08 <action name="foo" class="mypackage.fooAction"> 09 <result name="success" type="dispatcher">bar.jsp</result> 10 </action> 11 <action name="submitForm" class="mypackage.submitFormAction"> 12 <result name="success" type="dispatcher">submitSuccess.jsp</result> 13 </action> 14 ...... 15 </package>
【代码剖析】上述代码在第3行到第7行通过标签<global-results>配置了全局result。
5.4.3 拦截器配置
Interceptor(拦截器)是能在一个action执行的前后执行的代码。它是做Web应用程序时很有用的工具。最常见的由Interceptor实现的功能如:安全检查(确保访问者是登录用户)、跟踪日志(记录每个action)、效率瓶颈检查(记录每个action开始和结束的时间以检查程序中的瓶颈)。也可以把Interceptor连在一起组成Interceptor栈(interceptor-stack)。比如在action执行前同时做登录检查、安全检查和记录日志,可以定义一个Interceptor栈。Interceptor必须事先定义好,然后可以连在一起组成一个栈。实例5-7定义了一个Interceptor和一个Interceptor栈。
【实例5-7】Interceptor配置:struts.xml
01 <interceptors> 02 <!--拦截器--> 03 <interceptor name="security" 04 class="com.mycompany.security.SecurityInterceptor" /> 05 <!--拦截器栈--> 06 <interceptor-stack name="defaultComponentStack"> 07 <interceptor-ref name="component" /> 08 <interceptor-ref name="defaultStack" /> 09 </interceptor-stack> 10 </interceptors>
【代码剖析】上述代码在第3行通过标签<interceptor>配置了一个拦截器标签,而在第6行到第9行通过标签< interceptor-stack>配置了一个拦截器栈。如果想使用该拦截器,还必须在标签<action>中进行这样配置:
<!--使用拦截器--> <action name="VelocityCounter" class="com.opensymphony.webwork.example.counter.SimpleCounter"> <result name="success">...</result> <interceptor-ref name="defaultComponentStack" /> </action>
注意 引用名既可以是拦截器名也可以是栈名,对框架来说没有区别。
5.4.4 包配置
所谓packages(包)就是把actions、results、types、interceptors这些元素打包到一个逻辑单元中去,从概念上讲,packages更像一个程序中的对象,可以被其他子包重写,而且可以拥有自己独立的部分。name属性是packages的必填元素,它作为一个关键字被后边的包引用;extends元素是可选的,它允许包扩展一个和多个前边定义的包。Abstract元素是可选的,如抽象类和抽象函数一样,它是必须被继承的,可以声明一个不包含actions的package。包的配置如实例5-8所示。
【实例5-8】package配置:struts.xml
01 <!--扩展了default包--> 02 <package name="example" namespace="/example" extends="struts-default"> 03 <action name="HelloWorld" class="example.HelloWorld"> 04 <result>/example/HelloWorld.jsp</result> 05 </action> 06 <action name="Login" method="login " class="example.Login"> 07 <result name="input">/example/Login.jsp</result> 08 <result type="redirect-action">Menu</result> 09 </action> 10 <action name="*" class="example.ExampleSupport"> 11 <result>/example/blank.jsp</result> 12 </action> 13 </package>
【代码剖析】上述代码在第2行通过标签<package>进行了包配置,由于struts.xml文件是自上而下处理的,所有被扩展的包需要在扩展包前定义。
5.4.5 命名空间配置
命名空间(Namespace)属性允许把action配置分成不同的命名空间,这样可以使功能不同的action中使用相同的名字。默认命名空间用""(空字符串)表示。如果系统在指定的命名空间中没有找到某个action,就会到默认命名空间中查找。可以在所有用“extends”扩展的命名空间外配置全局action不指定命名空间。
Struts2中有以“/”命名的根命名空间,它是请求直接来自应用程序根路径的时候的命名空间。和其他命名空间一样,如果在根命名空间中没有所需的action别名,系统会回到默认命名空间中查找。如实例5-9所示,这里使用了默认命名空间、“/”和声明了的命名空间“barspace”。
【实例5-9】命名空间:struts.xml
01 <!--命名空间为空--> 02 <package name="default"> 03 <action name="foo" class="mypackage.simpleAction> 04 <result name="success" type="dispatcher">greeting.jsp</result> 05 </action> 06 <action name="bar" class="mypackage.simpleAction"> 07 <result name="success" type="dispatcher">bar1.jsp</result> 08 </action> 09 </package> 10 <!--命名空间为"/"--> 11 <package name="mypackage1" namespace="/"> 12 <action name="moo" class="mypackage.simpleActtion"> 13 <result name="success" type="dispatcher">moo.jsp</result> 14 </action> 15 </package> 16 <!--命名空间为"barspace"--> 17 <package name="mypackage2" namespace="/barspace"> 18 <action name="bar" class="mypackage.simpleAction"> 19 <result name="success" type="dispatcher">bar2.jsp</result> 20 </action> 21 </package>
【代码剖析】对于上述代码的命名空间配置,如果请求不同,则会访问不同的action。例如:
1)如果请求为/barspace/bar.action,系统首先查找'/barspace'命名空间,如果找到bar action便执行,如果没有找到,则继续到默认命名空间中查找。在这个例子中'/barspace'空间中存在bar别名,所以它会被执行,如果返回success结果,请求将指向bar2.jsp。
2)如果请求为/barspace/foo.action,系统会在/barspace空间中查找foo这个action,如果找不到,系统继续在默认命名空间中查找。除非指定成其他的,否则默认空间是""。在上面的例子中,/barspace空间中没有foo这个action,这样默认空间中的/foo.action会被找到并执行。
3)如果请求为/moo.action,系统会在根空间('/')中查找'moo' action别名,如果没找到再到默认空间中查找。在这个例子中,moo这个action别名存在,因此会被执行。如果返回success,请求指向bar2.jsp。
4)如果请求为'/foo.action',系统会在'/'空间中查找,找到后执行,如果没有,则继续查找默认空间。在本例中,foo这个action别名不存在于'/'空间中,所以系统会到默认空间中查找并执行。
命名空间只有一个级别。例如,如果url是'/barspace/myspace/bar.action',Struts2先试着查找'/barspace/myspace'(在本例中是不存在的)。接着就直接到默认空间中查找'bar'这个action别名。结果在默认空间中的bar会被执行。
命名空间也可以用来实现系统安全,例如action名字之前可以有个路径,可以在路径上使用J2EE声明式的安全限制。这种方式很容易实现和维护。
5.4.6 在struts.xml中定义Bean
在struts.xml中还可以作JavaBean的定义如下:
<!--在struts.xml中定义Bean--> <struts> <bean type="com.opensymphony.xwork2.ObjectFactory" name="myfactory" class="com.company.myapp.MyObjectFactory" /> </struts>
这里引出了反转控制的概念。反转控制全称是Inversion of Control,简称IoC,也称为依赖注入Dependency Injection,是当前非常热门的一个概念。反转控制是用来处理对象间的依赖关系而出现的。Struts2本身也提供了IoC容器的功能,不过连Struts2的开发小组自己也推荐用户直接使用Spring作为IoC容器。在struts.properties中有一项配置“struts.objectFactory”就是定义由谁来做IoC容器,默认值是Spring。关于IoC,本书将在第四篇重点介绍。
5.4.7 在struts.xml中使用通配符
当配置文件中action mapping的数量很多的时候,使用通配符是一个很好的办法,可以将一些相似的mapping绑在一起,用一个比较通用的mapping来表示。在路径中用*来代替变化的部分,而action的处理类和JSP中{1}刚好可以代替这个变量。
<!--在struts.xml中使用通配符--> <action name="/edit*" class="example.Edit{1}Action"> <result name="failure" path="/mainMenu.jsp"/> <result name="ok" path="/\{1\}.jsp"/> </action>
以上代码URI:editSubscription,editRegistration,都将复合条件匹配到这个Action配置,被替代的是Action映射中的变量属性和Action结果替换{1}。/editSubscription将对应的Action处理类是example.EditSubscriptionAction,result的JSP是Subscription.jsp,而/editRegistration请求将匹配example.EditRegistrationAction和Registration.jsp。
在Action Mapping和Action Result中,通配符匹配值可以被访问用{N}标记,N从1到9指示通配符匹配值的被替代属性。全部的请求URI可以被访问用{0}标记。