Pregunta Reflejo de Java: impacto de setAccessible (verdadero)


Estoy usando algunas anotaciones para establecer dinámicamente valores de campos en clases. Como quiero hacer esto independientemente de si es público, protegido o privado, soy una vocación setAccessible(true) en el objeto Field cada vez antes de llamar al set() método. Mi pregunta es qué tipo de impacto tiene el setAccessible() llamada tiene en el campo en sí?

Más específicamente, digamos que es un campo privado y este conjunto de llamadas de código setAccessible(true). Si algún otro lugar en el código fuera recuperar el mismo campo a través de la reflexión, ¿el campo ya estaría accesible? O lo hace el getDeclaredFields() y getDeclaredField() Los métodos devuelven nuevas instancias de un objeto Field cada vez?

Creo que otra forma de plantear la pregunta es si llamo setAccessible(true), ¿Cuán importante es volver a establecer el valor original después de que termine?


75
2018-05-17 15:33


origen


Respuestas:


Con setAccessible() cambias el comportamiento del AccessibleObject, es decir, el Field instancia, pero no el campo real de la clase. Aquí esta la documentación (extracto):

Un valor de true indica que el objeto reflejado debe suprimir las comprobaciones de control de acceso de lenguaje Java cuando se utiliza

Y un ejemplo ejecutable:

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));
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass));
    }

}

56
2018-05-17 15:43



los getDeclaredField método tiene que devolver un nuevo objeto cada vez, exactamente porque este objeto tiene el mutable accessible bandera. Entonces no hay necesidad de reiniciar la bandera. Puede encontrar todos los detalles en esta publicación en el blog.


27
2018-05-17 15:41



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
2017-11-23 09:45



Como otros carteles han indicado, setAccessible solo se aplica a esa instancia de su java.lang.reflect.Field, por lo que no es necesario establecer la accesibilidad a su estado original.

Sin embargo...

Si quieres tus llamadas a field.setAccessible(true) para ser persistente necesita usar métodos subyacentes en java.lang.Class y java.lang.reflect.Field. Los métodos públicos que te envían copias del Field ejemplo, entonces "olvida" después de cada vez que haces algo como 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();
        }
    }
}

0
2018-03-12 00:42