0

编辑:我进行了更改,因为我认为我的问题可能缺少一些技术细节

该问题出现在 Struts 1.3.x 中,描述如下:

当修改与 Struts 相关的类(例如 ActionForm、Action 和/或我与它们一起使用的任何类)时,我会在测试我的修改时得到 ClassCastException(无需重新启动我的 webapp)。

如果我在进行相同的修改后重新启动我的 web 应用程序,那么没有例外,并且在测试时所做的更改是可见的。

由于以下原因,这种行为被认为是正常的:

如果进行了更改,我的 Web 容器 (weblogic) 配置为重新加载 servlet 和类

我假设对我的类进行修改时使用了不同的 ClassLoader。这可能会导致 ClassCastException。

因此,每次我想测试我的源代码修改时,我都必须重新启动 webapp...

我想知道的是我该怎么做(编程模式?最佳实践?)以避免这种 ClassCastException 或避免重新启动 webapp 以查看我的更改?

这是堆栈跟踪:

java.lang.ClassCastException: my.package.here.MyActionClassNameHere
    at org.apache.struts.chain.commands.servlet.CreateAction.getAction(CreateAction.java:65)
    at org.apache.struts.chain.commands.AbstractCreateAction.execute(AbstractCreateAction.java:91)
    at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
    at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
    at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
    at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
    at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
    at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1914)
    at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:463)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:821)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3650)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2174)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1446)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)

我看过关于页面的JRebel 。这可能是一个解决方案,但我还没有准备好将它用于我的目的(这是使用 struts 1.3.x 进行的一些非常简单的测试)。

我将继续做一些并行测试,以更多地了解这个异常......

4

2 回答 2

1

这是您在某个时间点将一个类的实例加载到两个单独的类加载器中时所承担的风险。请参阅http://zeroturnaround.com/blog/reloading-objects-classes-classloaders/并查找 ClassCastException。文章很好的解释了Java中类加载的核心。

至于 JRebel 以外的解决方案,您可能只需重新部署应用程序即可。您说应用程序足够小,因此不会花费太多时间。

于 2012-07-25T08:07:00.227 回答
0

默认情况下,Struts 1.3(最新的 1.3.x)将操作类放在缓存中以优化性能(重新启动部署清除缓存)。这就是异常的原因,因为我修改的类是由与缓存中的前一个不同的类加载器加载的。

此异常发生在检索动作实例的步骤中(在堆栈跟踪中从此处开始)。

at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)

默认情况下,这个版本的 Struts 实现了责任链模式(使用 xml 配置文件 chain-config.xml)。因此,上面提到的请求处理器将动作实例检索(或实例化)委托给CreationAction实现(在 stactrace 中显示):

at org.apache.struts.chain.commands.servlet.CreateAction.getAction(CreateAction.java:65)

以下是在进行更改并且不想重新启动部署时避免 ClassCastException 的解决方案。

解决方案 1:

  • 使用自定义类扩展 CreateAction 并重新定义其 getAction() 方法

  • 在 getAction() 中:如果super.getAction().getClass()的类加载器与新的类加载器不同,则通过调用super.createAction() 使其返回一个新实例(这意味着我应该知道合格的类名使用 == 运算符进行比较时我的 Action 类,否则我不应该比较并始终使用 createAction 加载新的动作类)

  • 使用自定义的chain-config.xml来指定使用自定义的CreationAction而不是默认的

解决方案 2:使用自定义类扩展RequestProcessor并使用它而不是默认类(即更新 struts-config.xml 中的参数)

我认为只有在开发阶段才应该这样做(以节省一些测试时间),并且出于性能(和安全)原因应该避免在生产阶段。

我没有空闲时间来添加更详细的示例代码、配置文件等......我会尝试有一天(在其他地方)这样做。但可以肯定的是,我测试了这些,它为我的目的工作:)

于 2012-07-28T15:54:59.183 回答