通过ASM Java字节码访问外部类的私有字段时获取java.lang.IllegalAccessError

时间:2010-09-17 18:40:56

标签: java reflection assembly bytecode java-bytecode-asm

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

有任何线索吗?谢谢你,

2 个答案:

答案 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。可悲的是,你的选择是

  1. 使字段可访问
  2. 使用反射
  3. 生成合成访问者