我正在通过JMX监视在Tomcat中运行的servlet,以及在Tomcat的同一个实例中运行的另一个servlet。当我设置get()以返回标准Java数据类型(String,int,byte []等)时,这很好。但是当我使用用户定义的类时,我得到一个ClassCastException,它给出了这条消息:
java.lang.ClassCastException: blah.My_UserDefinedClass cannot be cast to blah.My_UserDefinedClass
我很确定这是因为检测和管理层(分别是受监控的 servlet和监控 servlet)中的不同类加载器。我已经仔细检查了包含每个servlet的用户定义类的.jar文件,并且两个jar文件彼此相同。
我正在使用标准MBean,并设置了受监控的servlet以返回此属性:
public interface MyMonitorMBean
{
public My_UserDefinedClass getAllData();
}
实现:
public class MyMonitor implements MyMonitorMBean
{
private My_UserDefinedClass mAllData;
@Override
public My_UserDefinedClass getAllData()
{
return mAllData;
}
}
管理servlet中用于访问此数据的代码:
private void getAllDataFromMBean()
{
try
{
// this line generates the ClassCastException
My_UserDefinedClass allData = (My_UserDefinedClass)mMBS.getAttribute( mObjectName, "AllData" );
}
catch( Exception e )
{
}
}
虽然每次发送标准Java数据类型都可以生成多个get(),但我想构建/使用我自己的POD / POJO类,它封装了所有各种标准位和bob(标准Java数据类型)我想发回去,以便我可以在一次通话中获取我的数据。
有什么想法吗?
谢谢,
比尔
使用tomcat7,java6,windows xp,32bit
答案 0 :(得分:0)
这可能对你有用。您需要将[临时]线程的上下文类加载器设置为MBean的类加载器。您可以通过从MBeanServer请求类加载器实例来确定这一点。
private void getAllDataFromMBean()
{
try
{
final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader mbeanClassLoader = mMBS.getClassLoaderFor(mObjectName);
Thread.currentThread().setContextClassLoader(mbeanClassLoader);
My_UserDefinedClass allData = (My_UserDefinedClass)mMBS.getAttribute( mObjectName, "AllData" );
// Process allData here
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
catch( Exception e )
{
}
}
====更新====
还有一个密切相关的问题在这里得到了更全面的回答:ClassCastException when casting to the same class。
====更新====
我对你的确切设置并不完全清楚,但不管怎样,你肯定有一个类加载器问题,在运行时,你有[至少]两个不同的类加载器加载了My_UserDefinedClass。
这些是一些选择:
<强>生命周期强>
确保定义getAllDataFromMBean的类在同一个类加载器中进行了类加载,因此只有一个类加载器。 (可能很棘手)
或者,在加载之前,将包含该类的jar放入系统类路径中。 (即引导类路径或系统类路径)
<强>反射强>
这里有类加载器的核心问题,因为你的 getAllDataFromMBean 方法在一个类中,只要它加载就会触发一个My_UserDefinedClass的加载,所以正确蝙蝠,你有两个不同的类加载器,因此不兼容的类型。因此,由于底层值都是标准java类型,因此您可以反射性地访问该实例。
**一般化**
不要为您的值创建用户定义的类,而是将它们保存在名称/值映射中。这样,传递的所有数据都是核心java类型。
**外化**
如果您强制将用户定义的实例序列化(比如一个byte []),然后在读取端反序列化它,您将绕过此问题。
**打开类型**
这是我认为最好的解决方案。将您的类型定义为CompositeData。 这样,根本没有自定义类依赖。这增加了一些优点,例如您可以在远程JMX客户端(如JConsole等)中显示值,该客户端甚至在其类路径中没有自定义类。
//尼古拉斯