Struts2 Spring插件autoWire为no时仍按类型注入内部类问题咨询
环境信息
- Struts版本:2.5.30
Struts-Spring配置
<constant name="struts.objectFactory" value="spring" /> <!-- spring injection wont auto happen. Need to annotate with @autowired --> <constant name="struts.objectFactory.spring.autoWire" value="no" />
Action映射配置
<action name="loginAuthentication" class="com.webexchange.actions.security.Login" method="executeNoSecurity"> <interceptor-ref name="token" /> <interceptor-ref name="stackWithSecurity" /> <result name="success" type="redirectAction"> <param name="actionName">${forwardUrl}</param> <param name="namespace">${forwardNamespace}</param> <param name="struts.token.name">token</param> <param name="token">${token}</param> </result> <result name="input">/Login.jsp</result> <result name="invalid.token">/WEB-INF/jsp/centers/common/SystemActionErrors.jsp</result> </action>
引发问题的String类型Spring Bean
@Bean("passwordRuleString") public String passwordRuleString(@Autowired @Qualifier("passwordRuleList") List<String> passwordRuleList) { StringBuilder sb = new StringBuilder(); sb.append("Password must be "); boolean comma = false; for (String s : passwordRuleList){ if(comma) { sb.append(", "); } sb.append(s); comma = true; } return sb.toString(); }
问题现象
使用redirectAction类型结果时,Struts会调用org.apache.struts2.result.ServletActionRedirectResult的四参数构造函数,该构造函数的所有字符串参数都被上述String Bean实例注入。尽管已配置struts.objectFactory.spring.autoWire="no"(文档说明此配置关闭外部自动装配,仅保留注解驱动和*Aware接口注入),但问题仍发生。
多数参数在构造后会被覆盖,但methodName值保留,导致重定向URL变为:
/myNamespace/myAction.action!Password must be Min length of 5, Contain at least 1 Number, Contain at least 1 Letter
而非预期的:
/myNamespace/myAction.action
最终引发应用404错误。
疑问解答
有没有配置能阻止Struts内部按类型自动注入?
单靠struts.objectFactory.spring.autoWire="no"挡不住这种情况,可以试试这两个办法:
- 别直接在Spring容器里注册String类型Bean,把这类字符串封装到自定义配置类里。比如写个
PasswordConfig类,把规则字符串作为它的属性,再把这个配置类注册成Bean,用的时候注入这个配置类就行,代码示例如下:
@Bean public PasswordConfig passwordConfig(@Autowired @Qualifier("passwordRuleList") List<String> passwordRuleList) { PasswordConfig config = new PasswordConfig(); StringBuilder sb = new StringBuilder("Password must be "); boolean comma = false; for (String s : passwordRuleList){ if(comma) sb.append(", "); sb.append(s); comma = true; } config.setRuleString(sb.toString()); return config; } class PasswordConfig { private String ruleString; // 添加上getter和setter }
- 或者给那个String Bean设置
autowireCandidate = false,让Spring自动注入时忽略它。注解方式的话,修改@Bean注解:@Bean(name = "passwordRuleString", autowireCandidate = false)。
这算Bug吗?
严格来说不算Bug,属于框架整合的边界场景问题。当Struts用Spring当对象工厂时,Spring的构造注入逻辑会起作用——哪怕你设了autoWire="no",如果构造函数参数对应的类型在Spring容器里有Bean,Spring还是可能会自动匹配注入。Struts文档没把这种基础类型Bean的注入情况说清楚,才导致出现预期外的问题。
用Struts时只能创建自定义类的Spring Bean?
不是必须,但强烈不建议直接注册String、Integer这类基础类型的Bean。框架内部很多组件(比如Struts的Result、Interceptor)的构造函数里会用到这些基础类型,Spring会优先按类型匹配注入,很容易搞出莫名其妙的问题。如果要管理这类配置,最好封装到自定义类里,通过类属性来暴露值,既清晰又不会冲突。
内容的提问来源于stack exchange,提问作者Paul Zepernick

