Java Reflection - Auswirkung von setAccessible (true)

105

Ich verwende einige Anmerkungen, um Werte von Feldern in Klassen dynamisch festzulegen. Da ich dies unabhängig davon tun möchte, ob es öffentlich, geschützt oder privat ist, rufe ich setAccessible(true)das Field-Objekt jedes Mal auf, bevor ich die set()Methode aufrufe. Meine Frage ist, welche Auswirkungen der setAccessible()Anruf auf das Feld selbst hat.

Angenommen, es handelt sich um ein privates Feld, und dieser Satz von Code ruft auf setAccessible(true). Wenn an einer anderen Stelle im Code dasselbe Feld durch Reflexion abgerufen werden würde, wäre das Feld dann bereits zugänglich? Oder geben die Methoden getDeclaredFields()und getDeclaredField()jedes Mal neue Instanzen eines Field-Objekts zurück?

Ich denke, eine andere Möglichkeit, die Frage zu stellen, ist, wenn ich anrufe setAccessible(true), wie wichtig es ist, sie nach Abschluss auf den ursprünglichen Wert zurückzusetzen.

dnc253
quelle

Antworten:

85

Mit setAccessible()ändern Sie das Verhalten der AccessibleObject, dh der FieldInstanz, aber nicht das eigentliche Feld der Klasse. Hier ist die Dokumentation (Auszug):

Der Wert von truegibt an, dass das reflektierte Objekt bei Verwendung die Überprüfung der Java-Sprachzugriffskontrolle unterdrücken soll

Und ein lauffähiges Beispiel:

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
    }

}
Moritz Petersen
quelle
@PhilipRego Sie müssen die Importdeklarationen selbst schreiben. Ich hoffe, dass Sie wissen, wie das geht.
Moritz Petersen
Problem gefunden. Sie müssen NoSuchFieldException oder übergeordnetes Element auslösen oder verarbeiten.
Philip Rego
Ja, das ist nur ein Beispielcode. Ich meine, throws Exceptionbehandelt auch NoSuchFieldException, aber Sie möchten vielleicht etwas ausführlicher damit umgehen.
Moritz Petersen
Ich erhalte die Ausnahme für: Feld field1 = myClass.getClass (). GetDeclaredField ("theField"); es wird also nicht einmal kompiliert, dh setAccessible spielt keine Rolle?
user2796104
32

Die getDeclaredFieldMethode muss jedes Mal ein neues Objekt zurückgeben, genau weil dieses Objekt das veränderbare accessibleFlag hat. Das Flag muss also nicht zurückgesetzt werden. Die vollständigen Details finden Sie in diesem Blogbeitrag .

Jörn Horstmann
quelle
3

Wie andere Poster angegeben haben, setAccessiblegilt dies nur für diese Instanz von Ihnen. Daher java.lang.reflect.Fieldist es nicht erforderlich, die Barrierefreiheit auf den ursprünglichen Zustand zurückzusetzen.

Jedoch...

Wenn Sie möchten, dass Ihre Aufrufe field.setAccessible(true)dauerhaft sind, müssen Sie die zugrunde liegenden Methoden in java.lang.Classund verwenden java.lang.reflect.Field. Die öffentlich zugänglichen Methoden senden Ihnen Kopien der FieldInstanz, sodass sie nach jedem Vorgang "vergisst"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();
        }
    }
}

Update : Diese Implementierung ist für Java 8, zukünftige Versionen ändern das Backend, wodurch dies unterbrochen wird. Das gleiche Konzept gilt jedoch weiterhin, falls Sie diese Strategie wirklich fortsetzen möchten.

Col-E
quelle
-1
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));
    }

}
RamChandra Bhakar
quelle