简体   繁体   English

Java反射 - 访问受保护的字段

[英]Java reflection - access protected field

如何通过反射从对象访问继承的受保护字段?

Two issues you may be having issues with - the field might not be accessible normally (private), and it's not in the class you are looking at, but somewhere up the hierarchy. 您可能遇到的两个问题 - 该字段可能无法正常访问(私有),并且它不在您正在查看的类中,而是在层次结构的某个位置。

Something like this would work even with those issues: 即使这些问题,这样的事情也会起作用:

public class SomeExample {

  public static void main(String[] args) throws Exception{
    Object myObj = new SomeDerivedClass(1234);
    Class myClass = myObj.getClass();
    Field myField = getField(myClass, "value");
    myField.setAccessible(true); //required if field is not normally accessible
    System.out.println("value: " + myField.get(myObj));
  }

  private static Field getField(Class clazz, String fieldName)
        throws NoSuchFieldException {
    try {
      return clazz.getDeclaredField(fieldName);
    } catch (NoSuchFieldException e) {
      Class superClass = clazz.getSuperclass();
      if (superClass == null) {
        throw e;
      } else {
        return getField(superClass, fieldName);
      }
    }
  }
}

class SomeBaseClass {
  private Integer value;

  SomeBaseClass(Integer value) {
    this.value = value;
  }
}

class SomeDerivedClass extends SomeBaseClass {
  SomeDerivedClass(Integer value) {
    super(value);
  }
}

Use reflection to access the members of the class instance, make them accessible and set their respective values. 使用反射来访问类实例的成员,使它们可访问并设置它们各自的值。 Of course you'd have to know the name of each member you want to change, but I guess that won't be a problem. 当然,你必须知道你想要改变的每个成员的名字,但我想这不会是一个问题。

public class ReflectionUtil {
    public static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class superClass = clazz.getSuperclass();
            if (superClass == null) {
                throw e;
            } else {
                return getField(superClass, fieldName);
            }
        }
    }
    public static void makeAccessible(Field field) {
        if (!Modifier.isPublic(field.getModifiers()) ||
            !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
        {
            field.setAccessible(true);
        }
    }
}

public class Application {
    public static void main(String[] args) throws Exception {
        KalaGameState obj = new KalaGameState();
        Field field = ReflectionUtil.getField(obj.getClass(), 'turn');
        ReflectionUtil.makeAccessible(field);
        field.setInt(obj, 666);
        System.out.println("turn is " + field.get(obj));
    }
}

使用Apache Commons lang3中的 FieldUtils.writeField(object, "fieldname", value, true)readField(object, "fieldname", true)

field = myclass.getDeclaredField("myname");
field.setAccessible(true);
field.set(myinstance, newvalue);

I didn't want to drag in more libraries so I made a pure one that worked for me. 我不想拖入更多的库,所以我做了一个适合我的纯粹库。 It is an extension of one of the methods from jweyrich: 它是jweyrich的一种方法的扩展:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.Random;
import java.util.UUID;

public abstract class POJOFiller {

    static final Random random = new Random();

    public static void fillObject(Object ob) {
        Class<? extends Object> clazz = ob.getClass();

        do {
            Field[] fields = clazz.getDeclaredFields();
            fillForFields(ob, fields);

            if (clazz.getSuperclass() == null) {
                return;
            }
            clazz = clazz.getSuperclass();

        } while (true);

    }

    private static void fillForFields(Object ob, Field[] fields) {
        for (Field field : fields) {
            field.setAccessible(true);

            if(Modifier.isFinal(field.getModifiers())) {
                continue;
            }

            try {
                field.set(ob, generateRandomValue(field.getType()));
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    }

    static Object generateRandomValue(Class<?> fieldType) {
        if (fieldType.equals(String.class)) {
            return UUID.randomUUID().toString();
        } else if (Date.class.isAssignableFrom(fieldType)) {
            return new Date(System.currentTimeMillis());
        } else if (Number.class.isAssignableFrom(fieldType)) {
            return random.nextInt(Byte.MAX_VALUE) + 1;
        } else if (fieldType.equals(Integer.TYPE)) {
            return random.nextInt();
        } else if (fieldType.equals(Long.TYPE)) {
            return random.nextInt();
        } else if (Enum.class.isAssignableFrom(fieldType)) {
            Object[] enumValues = fieldType.getEnumConstants();
            return enumValues[random.nextInt(enumValues.length)];
        } else if(fieldType.equals(Integer[].class)) {
            return new Integer[] {random.nextInt(), random.nextInt()};
        }
        else {
            throw new IllegalArgumentException("Cannot generate for " + fieldType);
        }
    }

}

If you are just getting the protected field 如果你刚刚获得受保护的领域

Field protectedfield = Myclazz.class.getSuperclass().getDeclaredField("num");

If you are using Eclipse Ctrl + Space will bring up a list of methods when you type a "." 如果您正在使用Eclipse Ctrl + Space将在您键入“。”时显示方法列表。 after the object 在对象之后

If using Spring, ReflectionTestUtils provides some handy tools that help out here with minimal effort. 如果使用Spring, ReflectionTestUtils提供了一些方便的工具,只需要很少的工作就可以帮助你。

For example, to get a protected field value that is known to be an int : 例如,要获取已知为int的受保护字段值:

int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField");

Or alternatively to set this field's value: 或者设置此字段的值:

ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue);

A generic utility method to run any getter in this or any superclass. 用于在此超类或任何超类中运行任何getter的通用实用程序方法。

Adapted from Marius's answer. 改编自马吕斯的答案。

public static Object RunGetter(String fieldname, Object o){
    Object result = null;
    boolean found = false;
    //Search this and all superclasses:
    for (Class<?> clas = o.getClass(); clas != null; clas = clas.getSuperclass()){
        if(found){
           break;
        }
        //Find the correct method:
        for (Method method : clas.getDeclaredMethods()){
            if(found){
                break;
            }
            //Method found:
            if ((method.getName().startsWith("get")) && (method.getName().length() == (fieldname.length() + 3))){ 
                if (method.getName().toLowerCase().endsWith(fieldname.toLowerCase())){                            
                    try{
                        result = method.invoke(o);  //Invoke Getter:
                        found = true;
                    } catch (IllegalAccessException | InvocationTargetException ex){
                        Logger.getLogger("").log(Level.SEVERE, "Could not determine method: " + method.getName(), ex);
                    }
                }
            }
        }
    }
    return result;
}

Hopefully that is useful to someone. 希望这对某人有用。

use this utility: 使用此实用程序:

import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Stream;

import static java.lang.String.format;

public final class ReflectionUtils {

    private ReflectionUtils() { }

    private static final String GETTER_PREFIX = "get";
    private static final String SETTER_PREFIX = "set";

    /**
     * Get name of getter
     *
     * @param fieldName fieldName
     * @return getter name
     */
    public static String getterByFieldName(String fieldName) {
        if (isStringNullOrEmpty(fieldName))
            return null;

        return convertFieldByAddingPrefix(fieldName, GETTER_PREFIX);
    }

    /**
     * Get name of setter
     *
     * @param fieldName fieldName
     * @return setter name
     */
    public static String setterByFieldName(String fieldName) {
        if (isStringNullOrEmpty(fieldName))
            return null;

        return convertFieldByAddingPrefix(fieldName, SETTER_PREFIX);
    }

    /**
     * Get the contents of the field with any access modifier
     *
     * @param obj obj
     * @param fieldName fieldName
     * @return content of field
     */
    public static Object getFieldContent(Object obj, String fieldName) {
        if (!isValidParams(obj, fieldName))
            return null;

        try {
            Field declaredField = getFieldAccessible(obj, fieldName);
            return declaredField.get(obj);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot get field content for field name: " + fieldName, e);
        }
    }

    /**
     * @param clazz clazz
     * @param fieldName fieldName
     * @return content static field
     */
    public static Object getStaticFieldContent(final Class<?> clazz, final String fieldName) {
        try {
            Field field = getFieldWithCheck(clazz, fieldName);
            field.setAccessible(true);
            return field.get(clazz);
        } catch (Exception e) {
            String exceptionMsg = format("Cannot find or get static field: '%s' from class: '%s'", fieldName, clazz);
            throw new RuntimeException(exceptionMsg, e);
        }
    }

    /**
     * Set the contents to the field with any access modifier
     *
     * @param obj obj
     * @param fieldName fieldName
     * @param value value
     */
    public static void setFieldContent(Object obj, String fieldName, Object value) {
        if (!isValidParams(obj, fieldName))
            return;

        try {
            Field declaredField = getFieldAccessible(obj, fieldName);
            declaredField.set(obj, value);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot set field content for field name: " + fieldName, e);
        }
    }

    /**
     * Call a method with any access modifier
     *
     * @param obj obj
     * @param methodName methodName
     * @return result of method
     */
    public static Object callMethod(Object obj, String methodName) {
        if (!isValidParams(obj, methodName))
            return null;

        try {
            Method method = obj.getClass().getMethod(methodName);
            method.setAccessible(true);
            return method.invoke(obj);
        } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new IllegalArgumentException("Cannot invoke method name: " + methodName, e);
        }
    }

    /**
     * Get all fields even from parent
     *
     * @param clazz clazz
     * @return array of fields
     */
    public static Field[] getAllFields(Class<?> clazz) {
        if (clazz == null) return null;

        List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        if (clazz.getSuperclass() != null) {
            // danger! Recursion
            fields.addAll(Arrays.asList(getAllFields(clazz.getSuperclass())));
        }
        return fields.toArray(new Field[] {});
    }

    /**
     * Get the Field from Object even from parent
     *
     * @param obj obj
     * @param fieldName fieldName
     * @return {@code Optional}
     */
    public static Optional<Field> getField(Object obj, String fieldName) {
        if (!isValidParams(obj, fieldName))
            return Optional.empty();

        Class<?> clazz = obj.getClass();
        return getField(clazz, fieldName);
    }

    /**
     * Get the Field from Class even from parent
     *
     * @param clazz clazz
     * @param fieldName fieldName
     * @return {@code Optional}
     */
    public static Optional<Field> getField(Class<?> clazz, String fieldName) {
        if (!isValidParams(clazz, fieldName))
            return Optional.empty();

        Field[] fields = getAllFields(clazz);
        return Stream.of(fields)
                .filter(x -> x.getName().equals(fieldName))
                .findFirst();
    }

    /**
     * @param clazz clazz
     * @param fieldName fieldName
     * @return Class
     */
    public static Class<?> getFieldType(Class<?> clazz, String fieldName) {
        return getFieldWithCheck(clazz, fieldName).getType();
    }

    /**
     * @param clazz clazz
     * @param fieldName fieldName
     * @return Field
     */
    public static Field getFieldWithCheck(Class<?> clazz, String fieldName) {
        return ReflectionUtils.getField(clazz, fieldName)
                .orElseThrow(() -> {
                    String msg = String.format("Cannot find field name: '%s' from class: '%s'", fieldName, clazz);
                    return new IllegalArgumentException(msg);
                });
    }

    /**
     * Get the field values with the types already listed according to the field type
     *
     * @param clazz clazz
     * @param fieldName fieldName
     * @param fieldValue fieldValue
     * @return value cast to specific field type
     */
    public static Object castFieldValueByClass(Class<?> clazz, String fieldName, Object fieldValue) {
        Field field = getField(clazz, fieldName)
                .orElseThrow(() -> new IllegalArgumentException(String.format("Cannot find field by name: '%s'", fieldName)));

        Class<?> fieldType = field.getType();

        return castFieldValueByType(fieldType, fieldValue);
    }

    /**
     * @param fieldType fieldType
     * @param fieldValue fieldValue
     * @return casted value
     */
    public static Object castFieldValueByType(Class<?> fieldType, Object fieldValue) {
        if (fieldType.isAssignableFrom(Boolean.class)) {
            if (fieldValue instanceof String) {
                return convertStringToBoolean((String) fieldValue);
            }
            if (fieldValue instanceof Number) {
                return !(fieldValue).equals(0);
            }
            return fieldValue;
        }

        else if (fieldType.isAssignableFrom(Double.class)) {
            if (fieldValue instanceof String) {
                return Double.valueOf((String)fieldValue);
            }
            return ((Number) fieldValue).doubleValue();
        }

        else if (fieldType.isAssignableFrom(Long.class)) {
            if (fieldValue instanceof String) {
                return Long.valueOf((String)fieldValue);
            }
            return ((Number) fieldValue).longValue();
        }

        else if (fieldType.isAssignableFrom(Float.class)) {
            if (fieldValue instanceof String) {
                return Float.valueOf((String)fieldValue);
            }
            return ((Number) fieldValue).floatValue();
        }

        else if (fieldType.isAssignableFrom(Integer.class)) {
            if (fieldValue instanceof String) {
                return Integer.valueOf((String)fieldValue);
            }
            return ((Number) fieldValue).intValue();
        }

        else if (fieldType.isAssignableFrom(Short.class)) {
            if (fieldValue instanceof String) {
                return Short.valueOf((String)fieldValue);
            }
            return ((Number) fieldValue).shortValue();
        }

        return fieldValue;
    }

    private static boolean convertStringToBoolean(String s) {
        String trim = s.trim();
        return !trim.equals("") && !trim.equals("0") && !trim.toLowerCase().equals("false");
    }

    private static boolean isValidParams(Object obj, String param) {
        return (obj != null && !isStringNullOrEmpty(param));
    }

    private static boolean isStringNullOrEmpty(String fieldName) {
        return fieldName == null || fieldName.trim().length() == 0;
    }

    private static Field getFieldAccessible(Object obj, String fieldName) {
        Optional<Field> optionalField = getField(obj, fieldName);
        return optionalField
                .map(el -> {
                    el.setAccessible(true);
                    return el;
                })
                .orElseThrow(() -> new IllegalArgumentException("Cannot find field name: " + fieldName));
    }

    private static String convertFieldByAddingPrefix(String fieldName, String prefix) {
        return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }
}

Do you perhaps mean from a different object an untrusted context with a SecurityManager set? 您是否可能从不同的对象意味着设置了SecurityManager的不受信任的上下文? That would break the type system, so you can't. 这会打破类型系统,所以你不能。 From a trusted context, you can call setAccessible to defeat the type system. 从可信上下文中,您可以调用setAccessible来打败类型系统。 Ideally, don't use reflection. 理想情况下,不要使用反射。

You could do something like... 你可以做点什么......

Class clazz = Class.forName("SuperclassObject");

Field fields[] = clazz.getDeclaredFields();

for (Field field : fields) {
    if (field.getName().equals("fieldImLookingFor")) {
        field.set...() // ... should be the type, eg. setDouble(12.34);
    }
}

You might also need to change the accessibility, as noted in Maurice's answer. 您可能还需要更改可访问性,如Maurice的回答中所述。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM