我正在将一个 struts 2.2.3.1 portlet 应用程序 (jsr168) 迁移到新版本 2.3.3。在大多数情况下,一切仍然正常,但有一个明显的问题。
我正在使用 package.properties 资源包来处理我的 jsp 中的标题和标签等内容。当我第一次点击操作时,会从资源包中检索消息并按预期显示。但是,当我对某个操作发出第二个请求时,会显示消息键,这通常意味着找不到资源。
(第一次执行操作和任何后续请求之间的主要区别似乎是,在第一个请求中,仅处理事件 portlet 阶段,而不是呈现 portlet 阶段。任何后续请求都使用这两个阶段。)
我花了很长时间才弄清楚发生了什么,但大致是这样的:
我开始逐步浏览org.apache.struts2.components.Text。当在 jsp 中遇到 struts 文本标记时使用此类。
end()方法使用来自TextProviderHelper的静态getText()方法。这就是我找到问题根源的地方:
for (Object o : stack.getRoot()) {
if (o instanceof TextProvider) {
tp = (TextProvider) o;
msg = tp.getText(key, null, args, stack);
break;
}
}
上面的代码遍历值堆栈,直到找到TextProvider。因为ActionSupport实现了 TextProvider,并且因为 struts 试图将动作放在堆栈顶部或附近,所以动作通常是提供者。
这是两个版本在不同时间的值堆栈中的对象:
版本 2.2.3.1
第一个请求值栈
- --> TestPortletAction
- 默认文本提供者
第二次请求值栈
- --> TestPortletAction
- 默认文本提供者
- DirectRenderFromEventAction
- 默认文本提供者
版本 2.3.3
第一个请求值栈
- --> TestPortletAction
- 默认文本提供者
第二次请求值栈
- DirectRenderFromEventAction
- 默认文本提供者
- --> TestPortletAction
- 默认文本提供者
因此,由于某种原因,在新版本中,在处理完渲染阶段后,动作类不在堆栈顶部。这意味着TextProviderHelper使用DefaultTextProvider,它确实尝试定位资源包......但这样做永远不会成功。
我对DefaultTextProvider如何被推入堆栈进行了一些调查:
Jsr168调度程序。serviceAction () 调用ActionProxyFactory来创建动作代理。就在返回代理之前,调用prepare (),然后调用DefaultActionInvocation的init方法。init 调用createContextMap () 来创建堆栈
stack = valueStackFactory.createValueStack();
使用的构造函数:
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
push(prov);
}
啊啊啊,这就是我到目前为止所遇到的问题。剩下的问题是:
- 第二个DefaultTextProvider如何以及在何处被推入堆栈?(真的有理由拥有其中两个吗?)
- DirectRenderFromEventAction如何以及在何处被推入堆栈?
- 新版本的什么变化导致了这种情况?
- 这是错误还是预期行为?
一个可能的解决方案(确实可以解决它)是使用 ognl 显式调用操作的getText方法,但我认为这并不理想,它确实意味着更改一堆 jsp 文件。
<s:property value = "%{getText('some.key')}"/>
可以在此处找到演示该问题的示例项目(这是一个 Maven 项目):
http://new-value-stack-order.googlecode.com/svn
我使用tomcat 6.0.25和pluto 1.1.7作为我的开发环境。