无法为 tomcat jvm 公开 Java 检测

Cannot expose Java Instrumentation for tomcat jvm

我在 运行 Java 应用程序中使用 tomcat7 实例。我的应用程序需要暴露 Java 检测。这是通过 javaagent 完成的,我在启动时将代理传递给 setenv.bat 脚本中的 JVM。

set JAVA_OPTS=%JAVA_OPTS% -javaagent:"C:\path\to\agent.jar"

在清单文件中我有所需的部分:

Premain-Class: package.name.agent.ExposeInstrumentation

在代理的 premain 方法中 class 将 JVM 提供的检测分配给可通过静态方法访问的静态变量

public final class ExposeInstrumentation {
private static Instrumentation s_instrumentation;
public static void premain(String arguments, Instrumentation instrumentation) {
    s_instrumentation = instrumentation;
}

public static Instrumentation getInstrumentation() {
    return s_instrumentation;
}
}

但是在我执行此操作时的代码中:

Instrumentation instrumentation = ExposeInstrumentation.getInstrumentation();

getInstrumentation() returns 空;

有什么问题?

更新

我做了一些进一步的调试并执行了 premain 并且 s_instrumentation 接收了检测,但是当我稍后在我的代码中调用 getInstrumentation 时 s_instumentation 被设置为 null。这很奇怪,我认为该值在程序的生命周期内仍然有效。

我假设您正在加载 class ExposeInstrumentation 两次。一次通过应用程序 class 加载器,即 child-first (reverse-order),一次通过 Java 代理,其中 class 由系统 class 加载器自动加载。结果,ExposeInstrumentation class 被加载两次,您从未设置该字段的应用程序访问那个。

您可以通过显式访问系统加载的 class 加载器 class 来解决此问题:

class ExposeInstrumentation {

  // public to assure accessability
  public static Instrumentation s_instrumentation;

  public static void premain(String arguments, Instrumentation inst) {
    s_instrumentation = inst;
  }

  public static Instrumentation getInstrumentation() {
    try {
      return (Instrumentation) ClassLoader.getSystemClassLoader()
          .loadClass(ExposeInstrumentation.class.getName())
          .getDeclaredField("s_instrumentation")
          .get(null);
    } catch(Exception e) {
      return null;
    }
  }
}

您还可以查看 Byte Buddy Agent project 提供此功能和更多(运行时安装)代理。使用 Byte Buddy,您可以简单地调用 ByteBuddyAgent.getInstrumentation().