由于其类加载策略,如何避免Struts 1.3.x中的ClassCastException?

时间:2012-07-20 15:29:37

标签: classloader classcastexception struts-1

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

问题出现在Struts 1.3.x中,并由以下内容描述:

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

如果我在进行相同的修改后重新启动我的webapp,那么没有异常,并且在测试时可以看到所做的更改。

由于以下原因,此行为被称为NORMAL:

我的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的一些非常简单的测试)。

我会继续做一些并行测试,以了解更多有关此异常的信息......

2 个答案:

答案 0 :(得分:1)

在某个时间点,如果您在两个单独的类加载器中加载了一个类的实例,则会产生这种风险。请参阅http://zeroturnaround.com/blog/reloading-objects-classes-classloaders/并查找ClassCastException。本文很好地解释了Java中类加载的核心。

除了JRebel之外的解决方案,您可能只需重新部署应用程序。你说应用程序足够小,所以不应该花太多时间。

答案 1 :(得分:0)

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

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

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

By default this version of Struts implements the chain of responsability pattern(使用xml配置文件chain-config.xml)。因此,request processor mentioned above委托动作实例检索(或实例化)到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加载新的动作类)

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

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

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

我没有空闲时间添加更多详细的示例代码,配置文件等...我会尝试在某一天(其他地方)进行。但我肯定会对这些进行测试,并且它符合我的目的:)