在 JNI DefineClass 中解决依赖关系
Resolving Dependencies in JNI DefineClass
我正在使用 JVMTI 编写应用程序。我正在尝试检测字节码:通过在每个方法条目上注入方法调用。
我知道该怎么做,但问题出在仪器 class 中,比如说它叫做 Proxy
,我使用 JNI 函数 DefineClass 加载它。我的 Proxy
在 Java Class 库中有一些依赖项,目前只有 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
;有替代方案吗?
我不认为解决标准 class java.lang.ThreadLocal
有问题,而是内部 class 扩展它,由
生成
new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return Boolean.FALSE;
}
};
由于内部和外部 class 之间的循环依赖,通过 DefineClass
解决这个问题可能确实是不可能的,所以没有允许定义它们的顺序,除非你有一个完整的ClassLoader
returns class 按需提供。
最简单的解决方案是完全避免生成内部 class,这可以通过 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);
}
您还可以将匿名内部 class 转换为顶级 class。从那时起,class 不再依赖于它以前的外部 class,首先定义 ThreadLocal
的子类型,然后再定义 class 使用它,应该可以解决问题.
我正在使用 JVMTI 编写应用程序。我正在尝试检测字节码:通过在每个方法条目上注入方法调用。
我知道该怎么做,但问题出在仪器 class 中,比如说它叫做 Proxy
,我使用 JNI 函数 DefineClass 加载它。我的 Proxy
在 Java Class 库中有一些依赖项,目前只有 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
;有替代方案吗?
我不认为解决标准 class java.lang.ThreadLocal
有问题,而是内部 class 扩展它,由
new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return Boolean.FALSE;
}
};
由于内部和外部 class 之间的循环依赖,通过 DefineClass
解决这个问题可能确实是不可能的,所以没有允许定义它们的顺序,除非你有一个完整的ClassLoader
returns class 按需提供。
最简单的解决方案是完全避免生成内部 class,这可以通过 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);
}
您还可以将匿名内部 class 转换为顶级 class。从那时起,class 不再依赖于它以前的外部 class,首先定义 ThreadLocal
的子类型,然后再定义 class 使用它,应该可以解决问题.