2

在反射中,可以通过 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”

有什么线索吗?感谢捆绑,

4

2 回答 2

3

你不能那样做。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.

于 2010-10-01T13:31:13.510 回答
0

The actual problem is that you cannot legally access those variables. This is because the JVM defined its access rules before Java had inner classes, so javac creates synthetic accessors for fields that it cannot legally access in the JVM but can in Java. For example,

class Sample {
    private int i = 0;
    class Inner {
        int foo = i;
    }
}

Then we can use javap to decompile the generated classes.

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

}

Notice the access$000(Sample) method that got generated in Sample and used from Sample.Inner. Sadly, your options are to either

  1. Make the field accessible
  2. Use reflection
  3. Generate synthetic accessors
于 2011-03-16T17:11:34.057 回答