java defineclass1_Java字节码检测:反射调用defineClass时出现NullPointerException

意图:

我正在使用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的一部分)中被初始化.

edit – MyFancyProfiler包含一个静态API,它将通过类似于in the solution to this question所描述的机制获得对系统其余部分的引用.引用作为Object获得,并且通过反射进行适当的调用,这样即使当前ClassLoader不知道底层类,它仍然可以工作.

难点

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

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

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

}

编辑更多信息 – 这种注入的原因是确保每个类,无论加载哪个ClassLoader,都可以直接调用MyFancyProfiler.methodEntered.一旦它进行调用,MyFancyProfiler将需要使用反射与系统的其余部分进行交互,否则我将在尝试直接引用时获得InvocationTargetException或NoClassDef异常.我目前正在使用它,以便MyFancyProfiler的唯一“直接”依赖项是JRE系统类,所以它似乎没问题.

问题

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

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,而pd是一个ProtectionDomain(据我所知)应该是“默认值” “保护域.所以我没有看到该行明显的方式导致NullPointerException.目前我很难过,我希望有人可以对此提供一些见解.

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

解决方法:

不要将代码注入ClassLoaders,而是尝试在bootstrap类加载器中加载包含MyFancyProfiler的jar.最简单的方法是将以下行添加到javaagent jar的清单中:

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

这将使所有类加载器都能访问该jar中的所有内容,包括我相信OSGi和朋友.

标签:java,classloader,osgi,instrumentation,jvmti

来源: https://codeday.me/bug/20190625/1285153.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值