mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-02-13 21:31:08 +08:00
support access private member of super class
This commit is contained in:
parent
121536a337
commit
4995bbd9a9
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user