在反射中,私有字段可以通过getDeclaredField()和setAccessible(true)进行访问。如何通过Objectweb ASM字节码API访问外部类的私有字段? 我设置通过
等方式获取私有字段Field current = sourceObject.getDeclaredField(privateFieldName);
Field.setAccessible(true);
Type sourceType = Type.getType(sourceObject.getClass());
mv.visitFieldInsn(Opcodes.GETFIELD,
sourceType.getInternalName(),
privateFieldname,
Type.getDescriptor(current.getType()));
当执行字节代码并获取私有字段时,我总是收到错误“java.lang.IllegalAccessError”
有任何线索吗?谢谢你,
答案 0 :(得分:2)
你不能这样做。 setAccessible(true)
只会影响当前程序执行中的当前字段引用(即它不会影响生成的修改程序的执行)。
要在运行修改后的程序时访问私有字段,您基本上必须将相应的反射步骤嵌入到程序中。
要访问存储在本地变量YourClass.thePrivatefield
中的某个对象的私有字段varId
,您可以执行类似
// Get hold of the field-reference
mv.visitLdcInsn(Type.getType("LYourClass;"));
mv.visitLdcInsn("thePrivateField");
mv.visitMethodInsn(INVOKEVIRTUAL,
"java/lang/Class",
"getDeclaredField",
"(Ljava/lang/String;)Ljava/lang/reflect/Field;");
// Duplicate the reference
mv.visitInsn(DUP);
// Call setAccessible(true) using the first reference.
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL,
"java/lang/reflect/Field",
"setAccessible",
"(Z)V");
// Call get(yourObject) using the second reference to the field.
mv.visitInsn(ALOAD, varId);
mv.visitMethodInsn(INVOKEVIRTUAL,
"java/lang/reflect/Field",
"get",
"(Ljava/lang/Object;)Ljava/lang/Object;");
如果您尝试访问的字段是您重写的cobe基础的一部分,您显然也可以使用ACC_PUBLIC
而不是ACC_PRIVATE
公开该字段。
答案 1 :(得分:0)
实际问题是您无法合法访问这些变量。这是因为JVM在Java具有内部类之前定义了它的访问规则,因此javac为它无法在JVM中合法访问但可以在Java中合法访问的字段创建合成访问器。例如,
class Sample {
private int i = 0;
class Inner {
int foo = i;
}
}
然后我们可以使用javap
来反编译生成的类。
fowles@morbo:/tmp$ javap -private Sample
Compiled from "Sample.java"
class Sample extends java.lang.Object{
private int i;
Sample();
static int access$000(Sample);
}
fowles@morbo:/tmp$ javap -c Sample.Inner
Compiled from "Sample.java"
class Sample$Inner extends java.lang.Object{
int foo;
final Sample this$0;
Sample$Inner(Sample);
Code:
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:LSample;
5: aload_0
6: invokespecial #2; //Method java/lang/Object."<init>":()V
9: aload_0
10: aload_0
11: getfield #1; //Field this$0:LSample;
14: invokestatic #3; //Method Sample.access$000:(LSample;)I
17: putfield #4; //Field foo:I
20: return
}
请注意access$000(Sample)
中生成的Sample
方法,并使用Sample.Inner
。可悲的是,你的选择是