我正在使用JVMTI编写应用程序。我正在尝试检测字节码:通过在每个方法条目上注入方法调用。
我知道如何做到这一点,但问题出在仪器类中,比如称为Proxy
,我使用JNI函数DefineClass加载。我的Proxy
在Java类库中有一些依赖项,目前只有java.lang.ThreadLocal<Boolean>
。
现在,说我有这个,inInstrumentMethod
是普通的boolean
:
public static void onEntry(int methodID)
{
if (inInstrumentMethod) {
return;
} else {
inInstrumentMethod = true;
}
System.out.println("Method ID: " + methodID);
inInstrumentMethod = false;
}
代码编译和工作。但是,如果我inInstrumentMethod
java.lang.ThreadLocal<Boolean>
,我会得到一个NoClassDefFoundError。代码:
private static ThreadLocal<Boolean> inInstrumentMethod = new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return Boolean.FALSE;
}
};
public static void onEntry(int methodID)
{
if (inInstrumentMethod.get()) {
return;
} else {
inInstrumentMethod.set(true);
}
System.out.println("Method ID: " + methodID);
inInstrumentMethod.set(false);
}
我的猜测是依赖关系未正确解析,并且java.lang.ThreadLocal
未加载(因此无法找到)。那么问题是,如何强制Java加载java.lang.ThreadLocal
?在这种情况下,我不认为我可以使用DefineClass
;还有其他选择吗?
答案 0 :(得分:1)
我认为解析标准类java.lang.ThreadLocal
时没有问题,而是由内部类扩展它,由
new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return Boolean.FALSE;
}
};
由于内部类和外部类之间存在循环依赖关系,因此通过DefineClass
解决此问题可能确实是不可能的,因此没有允许定义它们的顺序,除非你有一个完整的ClassLoader
按需返回类。
最简单的解决方案是避免生成内部类,这在Java 8中是可能的:
private static ThreadLocal<Boolean> inInstrumentMethod
= ThreadLocal.withInitial(() -> Boolean.FALSE);
如果您使用的是Java 8之前的版本,则不能以这种方式使用它,因此在这种情况下最好的解决方案是重写代码以接受默认值null
作为初始值,无需指定不同的初始值:
private static ThreadLocal<Boolean> inInstrumentMethod = new ThreadLocal<>();
public static void onEntry(int methodID)
{
if (inInstrumentMethod.get()!=null) {
return;
} else {
inInstrumentMethod.set(true);
}
System.out.println("Method ID: " + methodID);
inInstrumentMethod.set(null);
}
您还可以将该匿名内部类转换为顶级类。从那以后,该类与以前的外部类没有依赖关系,在使用它定义类之前首先定义ThreadLocal
的子类型,应该可以解决问题。