1. 属性
- name:包的名称必须是唯一的。必选。
- namespace:包的命名空间。可选。
- extends:通过这个属性可以继承其他包的配置。可选。
- abstract:定义包是否为抽象包。可选。
2. action的命名空间--namespace
命名空间
我们知道在java语言中使用包管理java文件。如果不使用包的方式进行管理很容易就会在一个项目中出现同名的java文件。同样的为了解决action同名的问题,struts2引入命名空间的概念。
来看看一个具体的action请求。
http://localhost:8080/struts2.package/user/login
- http://localhost:8080/ 是服务器地址与监听端口
- struts2.package 是项目名称
- user 是命名空间
- login 是action的名称
再来看看struts.xml中的配置
<package name="user" namespace="/user" extends="struts-default">
<action name="login">
</action>
</package>
从上图可以直观的看出package 的namespace和action的name如何组成一个action请求 。
默认命名空间
所有没有指定namespace的包下的action都属于默认命名空间。
所以当一个action请求找不到对应命名空间的包后都会到默认命名空间的包下找对应的action。如果再找不到才会报异常。
ps:根命名空间和默认命名空间不是同一概念。根命名空间的配置和action请求例子如下
<!-- 根命名空间 -->
<package name="root" namespace="/" extends="struts-default">
<action name="index">
<result >/index.jsp</result>
</action>
</package>
action请求:http://localhost:8080/struts2.package/index
实验
1.测试action请求* http://localhost:8080/struts2.package/user/test/login*
到底会访问以下哪个action
<package name="user/test" namespace="/user/test" extends="struts-default">
<action name="login">
<result>/action/user-test/login.jsp</result>
</action>
</package>
<package name="user" namespace="/user" extends="struts-default">
<action name="/test/login">
<result>/action/use/login.jsp</result>
</action>
</package>
ps:要想在action的name 中使用正斜杠[/]需要加入以下配置。
<!-- 设置常量 允许使用正斜杠[/] -->
<constant name="struts.enable.SlashesInActionNames" value="true"/>
上文代码块中的两个package下的action单独使用的时候都可以由http://localhost:8080/struts2.package/user/test/login访问到。那两个配置同时使用的结果呢?
第一次结果是访问的是 /user/test 命名空间下的name为login的action。
想到有可能是配置的顺序影响了结果,调换顺序再来一次(莫名押韵~)。
结果还是一样。
思考:struts2是如何解析一个像这样的action请求呢?
这里涉及到struts2如何解析url的部分,搜索到一篇文章《struts2请求过程源码分析》对strust2源代码的分析后知道:当一个请求匹配多个命名空间时,以命名空间(namespace)长度长的优先--长度长说明精度高
《struts2请求过程源码分析》地址
ps:这个实验引出的是struts的重点部分,这里由于篇幅限制暂不深入解析,仅分析解答项目中容易发生歧义的部分。
3. 抽象包--abstract
package还有一个概念就是抽象包。
抽象包不需要设置namespace因为包下不会配置action。抽象包不会管理具体的action,抽象包的主要作用是将其他包中相同配置的部分组织在一起然后供其他包继承重用,简化sturts2的配置。
抽象包的配置如下
<!-- 抽象包 -->
<package name="all" abstract="true" extends="struts-default">
<global-results>
<result name="my-index">/action/my/index.jsp</result>
</global-results>
</package>
抽象包中可以定义除action以外的其他配置,如全局的result或者拦截器等。抽象包中是不能有action配置,否则运行会报错。
4.配置简化--extends
经过前面的长篇大论,相信读者脑中多少知道包的作用--简化配置。而实现就是通过extends属性。
一个非package至少要继承struts-default这个struts2的默认包。这个包中定义的许多拦截实现了struts2的诸多功能,可以说少了这个包连最基本的访问页面都不能实现,更别说给Action的类注入参数等功能。
来看看struts-default包的配置。在struts2的jar包下可以找到,就在struts-default.xml文件中。
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
<interceptors>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<!-- 省略一万行 -->
</interceptors>
<default-interceptor-ref name="defaultStack"/>
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
</package>
想想如果这样的配置需要在每个package下都要复制一遍,整个struts.xml文件该多杂乱。显然通过重用的方式可以很好的解决这个问题。
另一方面,考虑到修改的情况。当每个包下有相同的配置的时候就会使得配置的改动变得繁琐。重用这些配置可以简化修改。
多继承
package同时可以继承多个包的配置。
<!-- 同时继承 user 和all 包 -->
<package name="multi-extends" namespace="/extends" extends="user,all">
<action name="index" class="struts2.packages.action.ExtendsAction">
<!-- 使用all包中定义的 全局result my-index-->
</action>
</package>
ExtendsAction类
package struts2. packages.action;
public class ExtendsAction {
public String execute(){
System.out.println("进入ExtendsAction");
return "my-index";
}
}
首先我们访问http://localhost:8080/struts2.package/extends/index
然后ExtendsAction返回的是my-index但是在multi-extends包下并未设置任何result,所以此时可以知道是调用了all包下定义的全局result(my-index)。
到目前为止都很好理解
现在我们访问
http://localhost:8080/struts2.package/extends/login
login这个action在multi-extends包并没有定义,但是我们继承的user包下定义了action是否在这里会其作用呢?
结果是不会,最终显示的页面是默认命名空间下的login指向的页面
显然继承不是继承所有的配置,action的配置是不会被继承的。每个action都是唯一的,没有必要在不同命名空间下有相同的action存在。
我们要在这里理解在struts2的配置中“继承”虽然和面向对象编程的“继承”都实现了重用,但两者还是有差别的。struts2中的继承时针对那些可重用的配置而言的,并不包括action。struts2中的继承也没有多态等特性。
代码示例
可以在github下载study项目中的struts2.package工程。查看示例源码
struts2.package项目地址
源码有任何bug欢迎提交pull request。
这篇文章昨天本来就应该写完的,又颓废了一个周日。