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

View File

@ -1,6 +1,7 @@
package com.alibaba.testable.core.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
@ -22,38 +23,63 @@ public class TypeUtil {
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
* @param availableConstructors available constructors
* @param clazz class to construct
* @param parameterTypes class to look for
* @return constructor which match the parameter classes
*/
public static Constructor<?> getConstructorByNameAndParameterTypes(Constructor<?>[] availableConstructors,
Class<?>[] parameterTypes) {
public static Constructor<?> getConstructorByParameterTypes(Class<?> clazz, Class<?>[] parameterTypes) {
Constructor<?>[] availableConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> c : availableConstructors) {
if (typeEquals(c.getParameterTypes(), parameterTypes)) {
return c;
}
}
return null;
if (clazz.getSuperclass() != null) {
return getConstructorByParameterTypes(clazz.getSuperclass(), parameterTypes);
} else {
return null;
}
}
/**
* get method by name and parameter matching
* @param availableMethods available methods
* @param clazz class contains methods
* @param methodName method to look for
* @param parameterTypes class to look for
* @return method which match the name and class
*/
public static Method getMethodByNameAndParameterTypes(Method[] availableMethods,
String methodName,
Class<?>[] parameterTypes) {
public static Method getMethodByNameAndParameterTypes(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
Method[] availableMethods = clazz.getDeclaredMethods();
for (Method m : availableMethods) {
if (m.getName().equals(methodName) && typeEquals(m.getParameterTypes(), parameterTypes)) {
return m;
}
}
return null;
if (clazz.getSuperclass() != null) {
return getMethodByNameAndParameterTypes(clazz.getSuperclass(), methodName, parameterTypes);
} else {
return null;
}
}
/**

View File

@ -210,7 +210,8 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
private void findAllPrivateMembers(Class<?> cls) {
Field[] fields = cls.getDeclaredFields();
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());
} else {
memberRecord.nonPrivateNorFinalFields.add(f.getName());
@ -218,12 +219,15 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
}
Method[] methods = cls.getDeclaredMethods();
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));
} else {
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) {