struts.xml中package的extends仅继承父包的interceptors、results等配置,不继承action标签;父包须设abstract="true",子包需自行定义action并正确配置namespace。

struts.xml 里 package 的 extends 属性到底继承什么
它只继承父 package 声明的 interceptors、results、global-results、global-exception-mappings 和 default-interceptor-ref,不继承 action 标签本身——子包必须自己定义 action。
常见错误是以为写了 extends="base" 就能直接用父包里定义的 <action name="login"></action>,结果 404。Struts2 不支持 action 级别的继承,只有配置结构复用。
- 父包必须设
abstract="true"才能被继承(否则启动报错Package [xxx] cannot be extended) - 父包里定义的
<result-type></result-type>或自定义拦截器栈,子包可直接引用名字,不用重复声明 - 如果子包也声明了同名
<result></result>(比如name="success"),会覆盖父包的定义,不是合并
为什么 extends="struts-default" 是几乎每个 package 的标配
因为 struts-default 是 Struts2 自带的抽象包,它预定义了最常用的拦截器栈 defaultStack、JSON/REST 相关 result 类型、常用异常映射等。不继承它,你的 action 默认连参数绑定、字段验证、类型转换都失效。
典型现象:action 方法执行了,但 String username 始终为 null,或者表单提交后页面空白没跳转——大概率是忘了 extends="struts-default" 或误写成 extends="default"(后者不存在)。
- 检查拼写:
struts-default中间是短横线,不是下划线或空格 - 不能写成
extends="struts-default, json-default":Struts2 不支持多继承,只能单继承,如需 JSON 支持,应继承json-default(它自己已继承struts-default) - 自定义拦截器栈若依赖
defaultStack里的拦截器(比如params),就必须确保父包最终链到struts-default
多个 package 继承同一父包时,interceptor 栈会不会冲突
不会。每个 package 的 interceptor-ref 是独立解析的,即使都引用 defaultStack,运行时也是各自创建拦截器实例链,互不影响。
真正容易出问题的是:在父包里改了 defaultStack 的定义(比如删掉 validation 拦截器),所有继承它的子包都会受影响——这不是冲突,是预期中的传递性变更。
- 调试技巧:启动时加 JVM 参数
-Dstruts.devMode=true,访问/struts/路径可查看所有 package 和拦截器栈展开结构 - 避免在父包中重定义
defaultStack,如需定制,建议新建栈名(如myStack),用<interceptor-stack name="myStack"></interceptor-stack>显式组装 - 如果子包要临时禁用某个拦截器(比如登录 action 不需要验证),应在 action 级用
<interceptor-ref name="basicStack"></interceptor-ref>替代全局引用
extends 配置生效但 action 找不到,可能卡在哪几个地方
最常忽略的是命名空间(namespace)和路径匹配逻辑。Struts2 匹配 action 时,是“先找 namespace 匹配的 package,再在该 package 内找 action name”,extends 只影响配置复用,不改变查找路径。
例如:父包 name="base"、namespace="/api",子包 name="user"、namespace="/api/v2"、extends="base"。此时子包里的 <action name="list"></action> 对应 URL 是 /api/v2/list.action,不是 /api/list.action。
- 检查
namespace是否以/开头(不加会变成相对命名空间,行为难预测) - 确认请求 URL 的路径前缀和 package 的
namespace完全一致(包括末尾斜杠) - 用
struts.devMode=true查看控制台日志,搜索Unable to find action,它会打印出 Struts2 实际尝试匹配的 package 名和 action 名 - 不要在子包里漏写
namespace:如果不写,它默认是""(根命名空间),和父包的/api完全无关
extends 是静态配置复用机制,不是面向对象继承;它不解决 action 复用,也不绕过命名空间隔离——这两点最容易让人反复踩坑。










