2010-09-17 4 views
2

в отражении частное поле может быть доступно через getDeclaredField() и setAccessible (true). Как получить доступ к закрытому полю внешнего класса через API-интерфейс byteec ASM Objectweb? я поставил, чтобы получить частное поле от чего-то, как, с помощьюПолучить java.lang.IllegalAccessError при доступе к частному полю внешнего класса через ASM Java Bytecode

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

Здравствуйте, любая мысль? Спасибо, –

+0

Да. См. Мой ответ. – aioobe

ответ

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;"); 

Если поле вы пытаетесь сделать доступным является частью Коб базы, что ваш Вы можете, очевидно, также публиковать это поле, используя ACC_PUBLIC вместо ACC_PRIVATE.

+0

Отлично. Я попробую. Кстати, это Java ASM или BCEL? Спасибо –

+1

ASM ............. – aioobe

0

Актуальной проблемой является то, что вы не можете юридически получить доступ к этим переменным. Это связано с тем, что JVM определил свои правила доступа до того, как Java имеет внутренние классы, поэтому javac создает синтетические средства доступа для полей, которые он не может юридически получить в JVM, но может на Java. Например,

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

Тогда мы можем использовать javap декомпилировать сгенерированные классы.

[email protected]:/tmp$ javap -private Sample 
Compiled from "Sample.java" 
class Sample extends java.lang.Object{ 
    private int i; 
    Sample(); 
    static int access$000(Sample); 
} 

[email protected]:/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. Генерация синтетических аксессоров