support access private member of super class

This commit is contained in:
金戟 2021-03-09 14:21:05 +08:00
parent 121536a337
commit 4995bbd9a9
3 changed files with 81 additions and 50 deletions

View File

@ -18,35 +18,37 @@ public class PrivateAccessor {
/** /**
* 读取任意类的私有字段 * 读取任意类的私有字段
* @param ref 目标对象 * @param ref 目标对象
* @param field 目标字段名 * @param fieldName 目标字段名
*/ */
public static <T> T get(Object ref, String field) { public static <T> T get(Object ref, String fieldName) {
try { try {
Field declaredField = ref.getClass().getDeclaredField(field); Field field = TypeUtil.getFieldByName(ref.getClass(), fieldName);
declaredField.setAccessible(true); if (field == null) {
return (T)declaredField.get(ref); throw new MemberAccessException("Private field \"" + fieldName + "\" not exist");
}
field.setAccessible(true);
return (T)field.get(ref);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new MemberAccessException("Failed to access private field \"" + field + "\"", e); throw new MemberAccessException("Failed to access private field \"" + fieldName + "\"", e);
} catch (NoSuchFieldException e) {
throw new MemberAccessException("Private field \"" + field + "\" not exist", e);
} }
} }
/** /**
* 修改任意类的私有字段或常量字段 * 修改任意类的私有字段或常量字段
* @param ref 目标对象 * @param ref 目标对象
* @param field 目标字段名 * @param fieldName 目标字段名
* @param value 目标值 * @param value 目标值
*/ */
public static <T> void set(Object ref, String field, T value) { public static <T> void set(Object ref, String fieldName, T value) {
try { try {
Field declaredField = ref.getClass().getDeclaredField(field); Field field = TypeUtil.getFieldByName(ref.getClass(), fieldName);
declaredField.setAccessible(true); if (field == null) {
declaredField.set(ref, value); throw new MemberAccessException("Private field \"" + fieldName + "\" not exist");
}
field.setAccessible(true);
field.set(ref, value);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new MemberAccessException("Failed to access private field \"" + field + "\"", e); throw new MemberAccessException("Failed to access private field \"" + fieldName + "\"", e);
} catch (NoSuchFieldException e) {
throw new MemberAccessException("Private field \"" + field + "\" not exist", e);
} }
} }
@ -59,8 +61,7 @@ public class PrivateAccessor {
public static <T> T invoke(Object ref, String method, Object... args) { public static <T> T invoke(Object ref, String method, Object... args) {
try { try {
Class<?>[] cls = TypeUtil.getClassesFromObjects(args); Class<?>[] cls = TypeUtil.getClassesFromObjects(args);
Method declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(ref.getClass().getDeclaredMethods(), Method declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(ref.getClass(), method, cls);
method, cls);
if (declaredMethod != null) { if (declaredMethod != null) {
declaredMethod.setAccessible(true); declaredMethod.setAccessible(true);
return (T)declaredMethod.invoke(ref, args); return (T)declaredMethod.invoke(ref, args);
@ -79,35 +80,37 @@ public class PrivateAccessor {
/** /**
* 读取任意类的静态私有字段 * 读取任意类的静态私有字段
* @param clazz 目标类型 * @param clazz 目标类型
* @param field 目标字段名 * @param fieldName 目标字段名
*/ */
public static <T> T getStatic(Class<?> clazz, String field) { public static <T> T getStatic(Class<?> clazz, String fieldName) {
try { try {
Field declaredField = clazz.getDeclaredField(field); Field field = TypeUtil.getFieldByName(clazz, fieldName);
declaredField.setAccessible(true); if (field == null) {
return (T)declaredField.get(null); throw new MemberAccessException("Private static field \"" + fieldName + "\" not exist");
}
field.setAccessible(true);
return (T)field.get(null);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new MemberAccessException("Failed to access private static field \"" + field + "\"", e); throw new MemberAccessException("Failed to access private static field \"" + fieldName + "\"", e);
} catch (NoSuchFieldException e) {
throw new MemberAccessException("Private static field \"" + field + "\" not exist", e);
} }
} }
/** /**
* 修改任意类的静态私有字段或静态常量字段 * 修改任意类的静态私有字段或静态常量字段
* @param clazz 目标类型 * @param clazz 目标类型
* @param field 目标字段名 * @param fieldName 目标字段名
* @param value 目标值 * @param value 目标值
*/ */
public static <T> void setStatic(Class<?> clazz, String field, T value) { public static <T> void setStatic(Class<?> clazz, String fieldName, T value) {
try { try {
Field declaredField = clazz.getDeclaredField(field); Field field = TypeUtil.getFieldByName(clazz, fieldName);
declaredField.setAccessible(true); if (field == null) {
declaredField.set(null, value); throw new MemberAccessException("Private static field \"" + fieldName + "\" not exist");
}
field.setAccessible(true);
field.set(null, value);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new MemberAccessException("Failed to access private static field \"" + field + "\"", e); throw new MemberAccessException("Failed to access private static field \"" + fieldName + "\"", e);
} catch (NoSuchFieldException e) {
throw new MemberAccessException("Private static field \"" + field + "\" not exist", e);
} }
} }
@ -120,15 +123,14 @@ public class PrivateAccessor {
public static <T> T invokeStatic(Class<?> clazz, String method, Object... args) { public static <T> T invokeStatic(Class<?> clazz, String method, Object... args) {
try { try {
Class<?>[] cls = TypeUtil.getClassesFromObjects(args); Class<?>[] cls = TypeUtil.getClassesFromObjects(args);
Method declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(clazz.getDeclaredMethods(), method, cls); Method declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(clazz, method, cls);
if (declaredMethod != null) { if (declaredMethod != null) {
declaredMethod.setAccessible(true); declaredMethod.setAccessible(true);
return (T)declaredMethod.invoke(null, args); return (T)declaredMethod.invoke(null, args);
} }
// fit kotlin companion object, will throw 'NoSuchFieldException' otherwise // fit kotlin companion object, will throw 'NoSuchFieldException' otherwise
Field companionClassField = clazz.getDeclaredField(KOTLIN_COMPANION_FIELD); Field companionClassField = clazz.getDeclaredField(KOTLIN_COMPANION_FIELD);
declaredMethod = TypeUtil.getMethodByNameAndParameterTypes( declaredMethod = TypeUtil.getMethodByNameAndParameterTypes(companionClassField.getType(), method, cls);
companionClassField.getType().getDeclaredMethods(), method, cls);
Object companionInstance = getStatic(clazz, KOTLIN_COMPANION_FIELD); Object companionInstance = getStatic(clazz, KOTLIN_COMPANION_FIELD);
if (declaredMethod != null && companionInstance != null) { if (declaredMethod != null && companionInstance != null) {
declaredMethod.setAccessible(true); declaredMethod.setAccessible(true);
@ -154,8 +156,7 @@ public class PrivateAccessor {
*/ */
public static <T> T construct(Class<?> clazz, Object... args) { public static <T> T construct(Class<?> clazz, Object... args) {
try { try {
Constructor<?> constructor = TypeUtil.getConstructorByNameAndParameterTypes(clazz.getDeclaredConstructors(), Constructor<?> constructor = TypeUtil.getConstructorByParameterTypes(clazz, TypeUtil.getClassesFromObjects(args));
TypeUtil.getClassesFromObjects(args));
if (constructor != null) { if (constructor != null) {
constructor.setAccessible(true); constructor.setAccessible(true);
return (T)constructor.newInstance(args); return (T)constructor.newInstance(args);

View File

@ -1,6 +1,7 @@
package com.alibaba.testable.core.util; package com.alibaba.testable.core.util;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
@ -22,39 +23,64 @@ public class TypeUtil {
return cs; return cs;
} }
/**
* get specified field from class or its parents
* @param clazz class contains fields
* @param field field to look for
* @return field which match the name
*/
public static Field getFieldByName(Class<?> clazz, String field) {
try {
return clazz.getDeclaredField(field);
} catch (NoSuchFieldException e) {
if (clazz.getSuperclass() != null) {
return getFieldByName(clazz.getSuperclass(), field);
} else {
return null;
}
}
}
/** /**
* get constructor by parameter matching * get constructor by parameter matching
* @param availableConstructors available constructors * @param clazz class to construct
* @param parameterTypes class to look for * @param parameterTypes class to look for
* @return constructor which match the parameter classes * @return constructor which match the parameter classes
*/ */
public static Constructor<?> getConstructorByNameAndParameterTypes(Constructor<?>[] availableConstructors, public static Constructor<?> getConstructorByParameterTypes(Class<?> clazz, Class<?>[] parameterTypes) {
Class<?>[] parameterTypes) { Constructor<?>[] availableConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> c : availableConstructors) { for (Constructor<?> c : availableConstructors) {
if (typeEquals(c.getParameterTypes(), parameterTypes)) { if (typeEquals(c.getParameterTypes(), parameterTypes)) {
return c; return c;
} }
} }
if (clazz.getSuperclass() != null) {
return getConstructorByParameterTypes(clazz.getSuperclass(), parameterTypes);
} else {
return null; return null;
} }
}
/** /**
* get method by name and parameter matching * get method by name and parameter matching
* @param availableMethods available methods * @param clazz class contains methods
* @param methodName method to look for * @param methodName method to look for
* @param parameterTypes class to look for * @param parameterTypes class to look for
* @return method which match the name and class * @return method which match the name and class
*/ */
public static Method getMethodByNameAndParameterTypes(Method[] availableMethods, public static Method getMethodByNameAndParameterTypes(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
String methodName, Method[] availableMethods = clazz.getDeclaredMethods();
Class<?>[] parameterTypes) {
for (Method m : availableMethods) { for (Method m : availableMethods) {
if (m.getName().equals(methodName) && typeEquals(m.getParameterTypes(), parameterTypes)) { if (m.getName().equals(methodName) && typeEquals(m.getParameterTypes(), parameterTypes)) {
return m; return m;
} }
} }
if (clazz.getSuperclass() != null) {
return getMethodByNameAndParameterTypes(clazz.getSuperclass(), methodName, parameterTypes);
} else {
return null; return null;
} }
}
/** /**
* type equals * type equals

View File

@ -210,7 +210,8 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
private void findAllPrivateMembers(Class<?> cls) { private void findAllPrivateMembers(Class<?> cls) {
Field[] fields = cls.getDeclaredFields(); Field[] fields = cls.getDeclaredFields();
for (Field f : fields) { for (Field f : fields) {
if (Modifier.isFinal(f.getModifiers()) || Modifier.isPrivate(f.getModifiers())) { if (Modifier.isFinal(f.getModifiers()) || Modifier.isPrivate(f.getModifiers())
|| Modifier.isProtected(f.getModifiers())) {
memberRecord.privateOrFinalFields.add(f.getName()); memberRecord.privateOrFinalFields.add(f.getName());
} else { } else {
memberRecord.nonPrivateNorFinalFields.add(f.getName()); memberRecord.nonPrivateNorFinalFields.add(f.getName());
@ -218,12 +219,15 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
} }
Method[] methods = cls.getDeclaredMethods(); Method[] methods = cls.getDeclaredMethods();
for (final Method m : methods) { for (final Method m : methods) {
if (Modifier.isPrivate(m.getModifiers())) { if (Modifier.isPrivate(m.getModifiers()) || Modifier.isProtected(m.getModifiers())) {
checkAndAdd(memberRecord.privateMethods, m.getName(), getParameterLength(m)); checkAndAdd(memberRecord.privateMethods, m.getName(), getParameterLength(m));
} else { } else {
checkAndAdd(memberRecord.nonPrivateMethods, m.getName(), getParameterLength(m)); checkAndAdd(memberRecord.nonPrivateMethods, m.getName(), getParameterLength(m));
} }
} }
if (cls.getSuperclass() != null) {
findAllPrivateMembers(cls.getSuperclass());
}
} }
private void checkAndAdd(Map<String, List<Integer>> map, String key, final int value) { private void checkAndAdd(Map<String, List<Integer>> map, String key, final int value) {