Apache Struts 配置参数详解
Struts 6.12.0 配置参数详解:配置体系,default.properties 内置 57 项默认值,devMode/action.extension/multipart 参数实测,拦截器与结果类型,全程实测。
Apache Struts 配置参数详解
目标:基于 struts2-core 6.12.0 的 jar 内 default.properties 与 struts-default.xml 实取,以及在 Ubuntu 22.04.5 + Tomcat 9.0.58(JVM 堆 -Xms256m -Xmx512m)环境的逐项实测整理。覆盖配置体系与加载顺序、核心常量参数(含默认值、配置写法与实测效果)、OGNL/UI/Spring 参数、内置拦截器与结果类型,以及参数修改后的生效方式与常见问题。所有命令与回显均实测取得。
一、配置体系概览
1.1 配置文件层级
| 层级 | 位置 | 作用 |
|---|---|---|
| default.properties | struts2-core jar 内 org/apache/struts2/default.properties | 框架内置默认值(共 57 个 struts.* 键) |
| struts-default.xml | struts2-core jar 内 | 拦截器、拦截器栈、结果类型声明 |
| struts.xml | 应用 src/main/resources/struts.xml(打包后在 WEB-INF/classes) | 应用配置:常量覆盖、package、action |
| web.xml | 应用 WEB-INF/web.xml | 注册 StrutsPrepareAndExecuteFilter 入口 |
加载顺序为从上到下,struts.xml 中的 <constant> 会覆盖 default.properties 的同名默认值。
1.2 查看内置默认参数
从本地 Maven 仓库的 jar 中直接解出 default.properties 查看:
cd /tmp && unzip -o -q /root/.m2/repository/org/apache/struts/struts2-core/6.12.0/struts2-core-6.12.0.jar org/apache/struts2/default.properties
grep -E '^struts\.(devMode|action\.extension|multipart\.(maxSize|saveDir)|enable\.DynamicMethodInvocation) *=' org/apache/struts2/default.properties
实测回显(关键 5 键的内置默认值):
struts.multipart.saveDir=
struts.multipart.maxSize=2097152
struts.action.extension=action,,
struts.enable.DynamicMethodInvocation = false
struts.devMode = false
统计全部键数量:
grep -c '^struts\.' org/apache/struts2/default.properties
实测回显:
57
💡 说明:default.properties 中共 57 个
struts.*键,下文按用途分组摘取实测值。注意文件中部分键的等号两侧带空格(如struts.devMode = false),为 jar 内原文,照实呈现。
二、核心常量参数详解
2.1 struts.devMode(开发模式)
- 内置默认值:
false - 作用:开启后 OGNL 安全策略使用 devMode 专属排除清单,异常信息更详细,便于开发排查。
- struts.xml 写法:
<constant name="struts.devMode" value="true"/>
实测:设置 true 并部署后,服务日志出现告警(journalctl -u tomcat9):
2026-10-12 05:18:07,504 WARN [Catalina-utility-1] ognl.SecurityMemberAccess (SecurityMemberAccess.java:544) - Working in devMode, using devMode excluded classes and packages!
生产环境必须关闭(false),该 WARN 也会随之消失。
2.2 struts.action.extension(Action 后缀)
- 内置默认值:
action,,(支持.action与无后缀) - 作用:限定哪些 URL 后缀会交给 Struts 处理,逗号分隔,末尾空项代表「无后缀也匹配」。
- struts.xml 写法:
<constant name="struts.action.extension" value="do,action,"/>
实测:设置为 do,action, 并重新打包部署后,.action 与 .do 均可访问:
curl -s -o /dev/null -w 'hello.action: HTTP %{http_code}\n' 'http://localhost:8080/struts2-demo/hello.action?name=X'
curl -s -o /dev/null -w 'hello.do: HTTP %{http_code}\n' 'http://localhost:8080/struts2-demo/hello.do?name=Y'
实测回显:
hello.action: HTTP 200
hello.do: HTTP 200
⚠️ 注意:若把空后缀去掉(如
do,action),无后缀 URL 将不再映射,依赖无后缀 URL 的单元测试会抛 NullPointerException,详见 8.1 节实测报错。
2.3 struts.multipart.maxSize(上传大小上限)
- 内置默认值:
2097152(2MB,整个请求的上限,单位字节) - 作用:限制 multipart 请求总大小,超限时上传拦截器捕获错误并返回 input。
- struts.xml 写法:
<constant name="struts.multipart.maxSize" value="10485760"/>
实测(默认值时上传 3MB,页面回显错误原文):
curl -s -F 'upload=@/tmp/big.bin;type=application/octet-stream' 'http://localhost:8080/struts2-demo/upload.action' | grep -A2 'errorMessage'
实测回显:
<ul class="errorMessage">
<li><span>Request exceeded allowed size limit! Max size allowed is: 2,097,152 but request was: 3,145,929!</span></li> </ul>
调整到 10485760 并重新打包部署后,同一文件上传成功:
curl -s -F 'upload=@/tmp/big.bin;type=application/octet-stream' 'http://localhost:8080/struts2-demo/upload.do' | grep -E 'Saved'
实测回显:
<p>Saved big.bin (3145728 bytes, content-type=application/octet-stream) to /tmp/big.bin</p>
2.4 struts.multipart.saveDir(上传临时目录)
- 内置默认值:空字符串(回退到 Web 应用临时目录 javax.servlet.context.tempdir)
- 作用:multipart 请求体在解析阶段的暂存目录。
- struts.xml 写法:
<constant name="struts.multipart.saveDir" value="/data/struts-upload-tmp"/>
实测:未设置时服务日志提示(INFO 级,不影响功能):
2026-10-12 05:21:37,936 INFO [http-nio-8080-exec-9] dispatcher.Dispatcher (Dispatcher.java:1027) - Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir
2.5 struts.enable.DynamicMethodInvocation(动态方法调用)
- 内置默认值:
false - 作用:控制
Action!method感叹号语法是否可用。关闭时该形态 URL 直接 404,是 Struts 6.x 的安全默认。 - struts.xml 写法:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
实测(保持默认关闭):
curl -s -o /dev/null -w 'HTTP %{http_code}\n' 'http://localhost:8080/struts2-demo/example/Login!execute.action'
实测回显:
HTTP 404
按方法分发推荐使用通配符映射(如 <action name="Login_*" method="{1}">),不建议打开 DMI。
三、OGNL 与表达式相关参数
jar 内 default.properties 实取值(grep 原文):
struts.ognl.allowStaticFieldAccess=true
struts.ognl.logMissingProperties=false
struts.ognl.expressionCacheType=wtlfu
struts.ognl.expressionCacheMaxSize=10000
struts.ognl.beanInfoCacheType=wtlfu
struts.ognl.beanInfoCacheMaxSize=10000
struts.ognl.expressionMaxLength=256
| 键 | 默认值 | 说明 |
|---|---|---|
| struts.ognl.allowStaticFieldAccess | true | 是否允许 OGNL 访问静态字段(如 @class@field) |
| struts.ognl.logMissingProperties | false | 目标对象缺失属性时是否记录日志 |
| struts.ognl.expressionCacheType | wtlfu | OGNL 表达式缓存类型 |
| struts.ognl.expressionCacheMaxSize | 10000 | 表达式缓存上限 |
| struts.ognl.beanInfoCacheType | wtlfu | BeanInfo 缓存类型 |
| struts.ognl.beanInfoCacheMaxSize | 10000 | BeanInfo 缓存上限 |
| struts.ognl.expressionMaxLength | 256 | OGNL 表达式最大长度,超长直接拒绝(安全防护) |
四、UI 与 URL 及其他参数
jar 内 default.properties 实取值(grep 原文):
struts.locale.validateRequestLocale=false
struts.ui.theme=xhtml
struts.ui.theme.expansion.token=~~~
struts.url.http.port = 80
struts.url.https.port = 443
struts.date.formatter=dateTimeFormatter
| 键 | 默认值 | 说明 |
|---|---|---|
| struts.locale.validateRequestLocale | false | 是否校验请求携带的 locale 参数合法性 |
| struts.ui.theme | xhtml | s: 标签默认渲染主题(另有 simple、css_xhtml 等) |
| struts.ui.theme.expansion.token | ~~~ | 主题展开 token |
| struts.url.http.port | 80 | s:url 生成 http 链接使用的端口 |
| struts.url.https.port | 443 | s:url 生成 https 链接使用的端口 |
| struts.date.formatter | dateTimeFormatter | s:date 渲染使用的格式化器 |
💡 实测佐证:默认 xhtml 主题下,s:form/s:file/s:submit 渲染出的登录表单带
tdLabel/label/formButton等布局结构(属 xhtml 主题自动生成的表格布局);切换主题时修改 struts.ui.theme 即可。
五、Spring 集成参数
jar 内 default.properties 实取值(grep 原文,未引入 Spring 依赖时不生效,保留默认即可):
struts.objectFactory.spring.autoWire = name
struts.objectFactory.spring.useClassCache = true
struts.objectFactory.spring.autoWire.alwaysRespect = false
struts.objectFactory.spring.enableAopSupport = false
| 键 | 默认值 | 说明 |
|---|---|---|
| struts.objectFactory.spring.autoWire | name | Spring 装配方式(name/byName/type/auto/constructor) |
| struts.objectFactory.spring.useClassCache | true | 是否缓存已创建的 bean 类 |
| struts.objectFactory.spring.autoWire.alwaysRespect | false | 装配时是否始终尊重 Spring bean 定义 |
| struts.objectFactory.spring.enableAopSupport | false | 是否启用 Spring AOP 支持 |
六、struts-default.xml:拦截器与结果类型
从 jar 中解出 struts-default.xml 查看:
cd /tmp && unzip -o -q /root/.m2/repository/org/apache/struts/struts2-core/6.12.0/struts2-core-6.12.0.jar struts-default.xml
grep -c 'interceptor name=' struts-default.xml
实测回显:
40
6.1 内置拦截器清单(实取节选 25 个)
grep -oE '<interceptor name="[^"]+" class="[^"]+"' struts-default.xml | head -25
实测回显:
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"
<interceptor name="coep" class="org.apache.struts2.interceptor.CoepInterceptor"
<interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"
<interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"
<interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor"
<interceptor name="coop" class="org.apache.struts2.interceptor.CoopInterceptor"
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor"
<interceptor name="csp" class="org.apache.struts2.interceptor.csp.CspInterceptor"
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"
<interceptor name="actionFileUpload" class="org.apache.struts2.interceptor.ActionFileUploadInterceptor"
<interceptor name="i18n" class="org.apache.struts2.interceptor.I18nInterceptor"
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"
<interceptor name="paramRemover" class="org.apache.struts2.interceptor.ParameterRemoverInterceptor"
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"
常用拦截器速记:params(请求参数注入 Action 属性)、fileUpload(multipart 解析)、i18n(request_locale 切换)、exception(异常映射)、token/tokenSession(防重复提交)、prepare(prepare() 前置回调)、validation(校验框架,未在节选中列出)。
6.2 默认拦截器栈 defaultStack(实取开头)
grep -A6 'interceptor-stack name="defaultStack"' struts-default.xml
实测回显:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="httpMethod"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="csp">
自定义 package 通过 extends="struts-default" 继承后,未显式声明拦截器时自动引用 defaultStack。
6.3 结果类型清单(实取节选)
grep -oE '<result-type name="[^"]+" class="[^"]+"' struts-default.xml | head -12
实测回显:
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"
<result-type name="dispatcher" class="org.apache.struts2.result.ServletDispatcherResult"
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"
<result-type name="httpheader" class="org.apache.struts2.result.HttpHeaderResult"
<result-type name="redirect" class="org.apache.struts2.result.ServletRedirectResult"
<result-type name="redirectAction" class="org.apache.struts2.result.ServletActionRedirectResult"
<result-type name="stream" class="org.apache.struts2.result.StreamResult"
<result-type name="plainText" class="org.apache.struts2.result.PlainTextResult"
<result-type name="postback" class="org.apache.struts2.result.PostbackResult"
| 结果类型 | 用途 | 本文实测位置 |
|---|---|---|
| dispatcher | 转发到 JSP(默认类型) | 各页面均为此类型 |
| redirect | 302 重定向到 URL | goHello.action → hello.action?name=Redirected |
| redirectAction | 302 重定向到另一 Action | foo.action 兜底 → example/HelloWorld.action |
| chain | Action 链(转发,非 302) | — |
| stream | 文件流下载 | — |
| freemarker / plainText / httpheader / postback | 模板 / 源码 / 响应头 / 回发 | — |
七、参数修改与生效方式
struts.xml 打包后位于 WAR 的 WEB-INF/classes 下,属于随应用加载的静态配置。修改常量后的生效流程(实测路径):
- 编辑
src/main/resources/struts.xml中的<constant>; - 重新打包:
mvn package; - 重新部署(复制 war 到 /var/lib/tomcat9/webapps/ 或走 Manager 的 undeploy + deploy);
- curl 验证新值生效(如 2.2 节
.do返回 200、2.3 节 10MB 上传成功)。
⚠️ 注意:仅改 Tomcat 上已解压目录里的 struts.xml 不可靠(应用重载时会被 war 覆盖),务必以「改源码 → 重新打包 → 重新部署」为准。
八、常见问题
8.1 action.extension 去掉空后缀导致单元测试 NPE
把 struts.action.extension 设为 do,action(末尾无空项)后执行 mvn package,surefire 报错原文:
testLoginSubmitInput(com.example.struts.example.LoginTest) Time elapsed: 0.302 sec <<< ERROR!
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project struts2-demo: There are test failures.
java.lang.NullPointerException: Cannot invoke "org.apache.struts2.dispatcher.mapper.ActionMapping.getNamespace()" because "mapping" is null
at org.apache.struts2.junit.StrutsTestCase.getActionProxy(StrutsTestCase.java:91)
at com.example.struts.example.LoginTest.testLoginSubmitInput(LoginTest.java:46)
定位:LoginTest.java 第 46 行使用无后缀 URL:ActionProxy proxy = getActionProxy("/example/Login");,空后缀被去掉后该 URL 不再映射任何 Action,mapping 为 null。
解决:保留空后缀,改回 value="do,action,",重新打包后 6 个测试全部通过(Tests run: 6, Failures: 0)。
8.2 日志出现 Working in devMode 告警
这是 struts.devMode=true 的正常提示(OGNL 使用 devMode 排除清单),不是故障。生产环境把 devMode 设为 false 后告警消失。
8.3 上传报 Request exceeded allowed size limit
上传大小超过 struts.multipart.maxSize(默认 2097152 字节)。按业务需要调大该常量(如 10485760)并重新打包部署;同时注意反向代理(如有)自身的请求体限制需一并放行。
九、参考链接
- 官方配置文件说明:https://struts.apache.org/core-developers/configuration-files
- 常量配置速查:https://struts.apache.org/core-developers/configuration-files#constant-configuration
- struts-default.xml 原文(GitHub):https://github.com/apache/struts/blob/master/core/src/main/resources/struts-default.xml
- 拦截器参考:https://struts.apache.org/core-developers/interceptors
- 结果类型参考:https://struts.apache.org/core-developers/result-types