Java 字节码检测 : NullPointerException in reflective call to defineClass

标签 java osgi classloader instrumentation jvmti

意图:

我正在使用 java.lang.instrument包来为 Java 程序创建一些工具。我的想法是,我通过这个系统使用字节码操作,以便在每个方法的开头和结尾添加方法调用。一般来说,修改后的 Java 方法如下所示:

public void whateverMethod(){
    MyFancyProfiler.methodEntered("whateverMethod");
    //the rest of the method as usual...
    MyFancyProfiler.methodExited("whateverMethod");
}

MyFancyProfiler是在 premain 期间初始化的相对复杂系统的入口点方法(属于 java.lang.instrument 的一部分)。

编辑 - MyFancyProfiler包含一个静态 API,该 API 将通过描述的机制获取对系统其余部分的引用 in the solution to this question .引用作为 Object 获得,并通过反射进行适当的调用,因此即使当前的 ClassLoader 不知道底层类,它仍然可以工作。

困难

对于简单的 Java 程序,该方法工作正常。对于“真正的”应用程序(如窗口应用程序,尤其是 RCP/OSGi 应用程序),我遇到了 ClassLoaders 的问题.一些ClassLoaders不知道如何找到 MyFancyProfiler类,因此当它尝试调用 MyFancyProfiler 中的静态方法时会抛出异常.

我的解决方案(以及我真正的问题所在)目前是“注入(inject)”MyFancyProfiler进入每个遇到的ClassLoader通过反射(reflection)调用defineClass .它的要点是:

public byte[] transform(ClassLoader loader, String className, /* etc... */) {
  if(/* this is the first time I've seen loader */){
    //try to look up `MyFancyProfiler` in `loader`.
    if(/* loader can't find my class */){
      // get the bytes for the MyFancyProfiler class
      // reflective call to 
      // loader.defineClass(
      //   "com.foo.bar.MyFancyProfiler", classBytes, 0, classBytes.length);
    }
  }
  // actually do class transformation via ASM bytecode manipulation
}

编辑以获取更多信息 - 这种注入(inject)的原因是为了确保每个类,无论加载它的是哪个类加载器,都能够调用 MyFancyProfiler.methodEntered直接地。一旦它发出那个电话,MyFancyProfiler将需要使用反射来与系统的其余部分进行交互,否则当它尝试直接引用时我会得到一个 InvocationTargetException 或 NoClassDef 异常。我目前让它工作,以便 MyFancyProfiler 的唯一“直接”依赖项 |是JRE系统类,所以看起来没问题。

问题

这甚至有效!大多数时候!但是对于我在尝试跟踪 Eclipse(从命令行启动 IDE)时遇到的至少两个独立的类加载器,我得到了一个 NullPointerException来自内部 ClassLoader.defineClass方法:

java.lang.NullPointerException
    at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:500)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    // at my code that calls the reflection

ClassLoader.java 的第 500 行是对 domains.add(pd) 的调用, 其中domains似乎是一个在构造函数时初始化的 Set,并且 pdProtectionDomain那(据我所知)应该是“默认”ProtectionDomain .所以我没有看到该行导致 NullPointerException 的明显方式.目前我很困惑,我希望有人可以对此提供一些见解。

什么会导致 defineClass以这种方式失败?如果没有明显的解决方案,您能否提供一种潜在的替代方法来解决我的整体问题?

最佳答案

不要将代码注入(inject)类加载器,而是尝试在引导类加载器中加载包含 MyFancyProfiler 的 jar。最简单的方法是将以下行添加到 javaagent jar 的 list 中:

Boot-Class-Path: fancy-profiler-bootstrap-stuff.jar

这将使所有类加载器都可以访问该 jar 中的所有内容,我相信包括 OSGi 和 friend 。

关于Java 字节码检测 : NullPointerException in reflective call to defineClass,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15692621/

相关文章:

Java Android Google map 更改缩放控制位置并将相机设置为标记

java - 在 Java 并行/多线程中创建现有代码

java - 如何在Java虚拟机中查找类的实例?

java - AspectJ - 在运行时使用自定义 ClassLoader 编织

reflection - Sun JVM 在运行时创建 sun.reflect.DelegatingClassLoader 的实例是什么?

引用另一个枚举的 Java 枚举

java - 有人可以解释如何实验重力值吗?

java - 如何解决 JPMS 和 OSGi Bundle for Eclipse plugin project an Tycho 之间的冲突

java - CQ5 - 吊带 :OsgiConfig service in Java class

java - 在自定义 CommandProvider 中获取完整/未转义/未拆分的命令字符串