From 4995bbd9a9b68f18ba813d8409cc98b3f162ef8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Tue, 9 Mar 2021 14:21:05 +0800 Subject: [PATCH] support access private member of super class --- .../core/accessor/PrivateAccessor.java | 79 ++++++++++--------- .../alibaba/testable/core/util/TypeUtil.java | 44 ++++++++--- .../EnablePrivateAccessTranslator.java | 8 +- 3 files changed, 81 insertions(+), 50 deletions(-) diff --git a/testable-core/src/main/java/com/alibaba/testable/core/accessor/PrivateAccessor.java b/testable-core/src/main/java/com/alibaba/testable/core/accessor/PrivateAccessor.java index 9016cac..78f090d 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/accessor/PrivateAccessor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/accessor/PrivateAccessor.java @@ -18,35 +18,37 @@ public class PrivateAccessor { /** * 读取任意类的私有字段 * @param ref 目标对象 - * @param field 目标字段名 + * @param fieldName 目标字段名 */ - public static T get(Object ref, String field) { + public static 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 void set(Object ref, String field, T value) { + public static 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 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 getStatic(Class clazz, String field) { + public static 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 void setStatic(Class clazz, String field, T value) { + public static 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 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 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); diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/TypeUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/TypeUtil.java index 13ac809..2dd699b 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/TypeUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/TypeUtil.java @@ -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; + } } /** diff --git a/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java b/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java index b5e45b9..11535f2 100644 --- a/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java +++ b/testable-processor/src/main/java/com/alibaba/testable/processor/translator/EnablePrivateAccessTranslator.java @@ -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> map, String key, final int value) {