Odbicie Java - wpływ setAccessible (true)


106

Używam adnotacji do dynamicznego ustawiania wartości pól w klasach. Ponieważ chcę to zrobić niezależnie od tego, czy jest to publiczne, chronione czy prywatne, za setAccessible(true)każdym razem przed wywołaniem set()metody wywołuję obiekt Field . Moje pytanie brzmi: jaki wpływ ma to setAccessible()wezwanie na samo pole?

Mówiąc dokładniej, powiedzmy, że jest to pole prywatne i ten zestaw wywołań kodu setAccessible(true). Gdyby jakieś inne miejsce w kodzie miało następnie pobrać to samo pole poprzez refleksję, czy to pole byłoby już dostępne? A może metody getDeclaredFields()i getDeclaredField()zwracają za każdym razem nowe wystąpienia obiektu Field?

Wydaje mi się, że innym sposobem zadawania pytania jest to, że jeśli zadzwonię setAccessible(true), jak ważne jest, aby przywrócić pierwotną wartość po zakończeniu?

Odpowiedzi:


85

Wraz setAccessible()ze zmianą zachowania AccessibleObject, czyli Fieldinstancji, ale nie rzeczywistego pola klasy. Oto dokumentacja (fragment):

Wartość truewskazuje, że odbity obiekt powinien pomijać sprawdzanie kontroli dostępu w języku Java, gdy jest używany

I działający przykład:

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
    }

}

@PhilipRego musisz samodzielnie napisać deklaracje importu. Mam nadzieję, że wiesz, jak to zrobić.
Moritz Petersen

Znaleziono problem. Musisz zgłosić lub obsłużyć NoSuchFieldException lub element nadrzędny.
Philip Rego

Tak, to tylko przykładowy kod. Mam na myśli throws Exceptiontakże uchwyty NoSuchFieldException, ale możesz zająć się tym w bardziej wyszukany sposób.
Moritz Petersen

Otrzymuję wyjątek w: Field field1 = myClass.getClass (). GetDeclaredField ("theField"); więc nawet się nie kompiluje, czyli setAccessible nie ma nawet znaczenia?
user2796104

32

getDeclaredFieldMetoda ma powrócić nowego obiektu za każdym razem, właśnie dlatego, że obiekt ten ma zmienny accessibleflagę. Nie ma więc potrzeby resetowania flagi. Wszystkie szczegóły znajdziesz w tym poście na blogu .


3

Jak wskazały inne plakaty, setAccessiblema zastosowanie tylko do tego wystąpienia java.lang.reflect.Field, więc przywracanie dostępności do pierwotnego stanu nie jest potrzebne.

Jednak...

Jeśli chcesz, aby wywołania field.setAccessible(true)były trwałe, musisz użyć podstawowych metod w java.lang.Classi java.lang.reflect.Field. Publiczność stoi metod wysłać kopie na Fieldprzykład, więc „zapomina” po każdym razem zrobić coś podobnegoclass.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();
        }
    }
}

Aktualizacja : ta implementacja jest przeznaczona dla Java 8, przyszłe wersje zmieniają zaplecze, co to zepsuje. Ta sama koncepcja ma jednak nadal zastosowanie, jeśli naprawdę chcesz kontynuować tę strategię.


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

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.