web-dev-qa-db-fra.com

Java réflexion - impact de setAccessible (true)

J'utilise des annotations pour définir de manière dynamique les valeurs des champs dans les classes. Étant donné que je souhaite le faire, qu'il soit public, protégé ou privé, je suis un appelant setAccessible(true) sur l'objet Field à chaque fois avant d'appeler la méthode set(). Ma question est de savoir quel type d’impact l’appel setAccessible() a sur le champ lui-même?

Plus précisément, supposons que ce soit un champ privé et que cet ensemble de codes appelle setAccessible(true). Si un autre endroit du code devait alors récupérer le même champ par réflexion, le champ serait-il déjà accessible? Ou bien les méthodes getDeclaredFields() et getDeclaredField() renvoient-elles chaque fois de nouvelles instances d'un objet Field?

J'imagine qu'une autre façon de poser la question est si j'appelle setAccessible(true), à quel point est-il important de rétablir la valeur d'origine une fois l'opération terminée?

95
dnc253

Avec setAccessible(), vous modifiez le comportement de l'instance AccessibleObject, c'est-à-dire l'instance Field, mais pas le champ réel de la classe. Voici le documentation (extrait):

Une valeur de true indique que l'objet reflété doit supprimer les contrôles pour Java d'accès au langage lorsqu'il est utilisé

Et un exemple exploitable:

public class FieldAccessible {
    public static class MyClass {
        private String theField;
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        Field field1 = myClass.getClass().getDeclaredField("theField");
        field1.setAccessible(true);
        System.out.println(field1.get(myClass)); // no exception
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass)); // IllegalAccessException
    }

}
77
Moritz Petersen

La méthode getDeclaredField doit renvoyer un nouvel objet à chaque fois, précisément parce que cet objet possède l'indicateur mutable accessible. Il n'est donc pas nécessaire de réinitialiser le drapeau. Vous pouvez trouver tous les détails dans cet article de blog .

29
Jörn Horstmann

Comme d'autres afficheurs l'ont indiqué, setAccessible ne s'applique qu'à cette instance de votre Java.lang.reflect.Field. Il n'est donc pas nécessaire de rétablir l'accessibilité à son état d'origine.

Toutefois...

Si vous souhaitez que vos appels à field.setAccessible(true) soient persistants, vous devez utiliser les méthodes sous-jacentes dans Java.lang.Class Et Java.lang.reflect.Field. Les méthodes faisant face au public vous envoient copies de l'instance Field, donc "oublie" après chaque fois que vous faites quelque chose comme class.getField(name)

import Java.lang.reflect.*;
import Sun.reflect.FieldAccessor;

public class Reflect {
    private static Method privateGetDeclaredFields;
    private static Method getFieldAccessor;

    public static Field[] fields(Class<?> clazz) throws Exception {
        return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
    }

    public static <T> T get(Object instance, Field field) throws Exception {
        return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
    }

    public static void set(Object instance, Field field, Object value) throws Exception {
        ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
    }

    static {
        try {
            // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
            privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
            privateGetDeclaredFields.setAccessible(true);
            getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
            getFieldAccessor.setAccessible(true);
        } catch (Exception e) {
            // Should only occur if the internals change.
            e.printStackTrace();
        }
    }
}
2
Col-E
import Java.lang.reflect.Field;
import Java.lang.reflect.Method;

public class PrivateVariableAcc {

    public static void main(String[] args) throws Exception {
        PrivateVarTest myClass = new PrivateVarTest();
        Field field1 = myClass.getClass().getDeclaredField("a");
        field1.setAccessible(true);
        System.out.println("This is access the private field-"
            + field1.get(myClass));
        Method mm = myClass.getClass().getDeclaredMethod("getA");
        mm.setAccessible(true);
        System.out.println("This is calling the private method-"
            + mm.invoke(myClass, null));
    }

}
0
RamChandra Bhakar