2

我们的应用程序通过 JMX 公开了一些方法,我们使用 JVisualVM 调用这些方法。

这通常工作得很好,但有时方法调用会在应用程序内部出现异常而中止。在这种情况下,JVisualVM 不会显示来自异常的错误消息,而是显示错误消息

java.rmi.UnmarshalException [...] error unmarshaling return; 
nested exception is: java.lang.ClassNotFoundException [...]

这是相当无益和令人困惑的;我们希望 JVisualVM 显示真正的错误消息。

到目前为止,我们发现了什么:

JMX 似乎会序列化和反序列化调用期间抛出的任何异常。然而,在我们的例子中,异常是不属于 JDK 的自定义异常。因此,当通过 JVisualVM 调用方法时,JVisualVM 无​​法显示异常,因为由于未知的自定义异常类导致反序列化失败。

现在,作为一种解决方法,我们将通过 JMX 公开的所有方法包装在一个try-catch块中

    throw new RuntimeException("Error invoking method:"+e);

这是可行的,因为它将任何异常转换为字符串,但看起来相当不优雅和冗长。

  • 是否有一些通用的方法来告诉 JMX 不序列化异常?诸如“始终将异常转换为字符串”之类的东西?
  • 我们使用 Spring 的MBeanExporter。Spring中是否有一种机制来处理这个问题?

编辑

我们知道我们可以配置 JVisualVM 来加载有问题的类。但是,我们希望 JVisualVM 在没有特殊配置的情况下工作。此外,它可能运行一个应用程序代码甚至不可用的系统。

4

2 回答 2

1

您可以尝试-Djava.rmi.server.codebase在 JVisualVM 上使用,以便它可以从远程 JVM 加载异常类吗?

编辑:如果你不想这样做,你可以使用一个方面来做你的 try-catch 块。然后你只需要在一个地方。如果您已经在使用 Spring,那么 Spring AOP 将使这变得非常容易。

于 2012-04-20T09:49:10.857 回答
1

看看java.io.Serializable。您可以实现一个名为writeReplace的可选方法,该方法可以放置在您的自定义异常中。返回原始消息的通用异常。当异常被序列化时,它将发送泛型。或者你也可以发送一个字符串,但我不确定这是否适用于 VisualVM。我想这取决于数据检查的严格程度。

在将对象写入流时需要指定要使用的替代对象的可序列化类应使用精确签名实现此特殊方法:

ANY-ACCESS-MODIFIER 对象 writeReplace() 抛出 ObjectStreamException;

===== 更新 =====

知道了。第 3 方库.... 总结.... 这里的挑战是调用setAttribute[s]并通过 JVisualVM 在 MBean 上调用会导致抛出 JVisualVM 的类路径不可用类型的异常。

如果您在 MBeanServer 上实现了某种拦截器,它可以捕获任何抛出的异常(可能是任何类名java.javax.开头的异常,并将它们作为 RuntimeExceptions 重新抛出,并带有表示原始异常的非常丰富的错误消息. (如果有用,您甚至可以包含堆栈跟踪的字符串渲染)。这基本上就是您现在正在做的事情,除了它具有集中在一个地方的优点并且不依赖于您如何实现您的类。

尽管标准 JMX 规范中仍然缺少 MBeanServer 拦截器,但您可以通过创建自己的 MBeanServer 实现来模仿它们,该实现只是简单地委托给真正的 MBeanServer,但是对于setAttribute[s]调用调用,您可以使用“catch-and-重新投掷”技术。这听起来像是大手术,但除了复杂的应用程序服务器 JMX 实现的棘手问题外,它实际上相当简单。

有关示例,请参阅此stackoverflow 帖子和此要点。

.....话虽如此,您正在使用 Spring,您可以指定注册 MBean 的确切 MBeanServer 实例,以及向 JVisualVM 使用的 JMXConnectorServer 公开的 MBeanServer。所以会更简单:

  1. 使用 gist 中概述的 MBeanServer 包装器思想,在 Spring 中构造它,将真正的 MBeanServer 作为委托注入
  2. 在 Spring JMX 导出器中引用包装器 bean。
  3. 在您的 JMXConnectorServer 中引用包装器 bean。

如果您使用自动引导的默认 JMXConnectorServer,则需要切换到使用Spring 定义的一个

基于 Spring 的方法还有一个额外的好处,即只有远程 MBeanServer 调用(如 JVisualVM 调用)会经历异常重写,而应用程序中的内部调用将获得真正的异常,因为它们可以直接转到真正的MBeanServer。

===== 又一次更新 =====

Spring JMX 支持确实有一个MBean 拦截器实现。我不知道,但当我在思考这个问题时,听起来像是 Spring 会实现的东西,而且它就是......

于 2012-04-20T21:47:59.197 回答