From 22f59fdcf9714e92da746c2c4979fbb7c7cf0db3 Mon Sep 17 00:00:00 2001 From: calvin1978 Date: Tue, 3 Jan 2017 14:49:07 +0800 Subject: [PATCH] =?UTF-8?q?#525=20=E5=8A=A0=E5=BC=BAUtils=20Modules?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=EF=BC=9A=E8=87=AA=E5=AE=B6=E7=9A=84Reflectio?= =?UTF-8?q?ns=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/utils/pom.xml | 8 +- ...pertiesUtil.java => SystemProperties.java} | 2 +- .../modules/utils/reflect/Reflections.java | 127 ++++++++++++++---- .../utils/reflect/ReflectionsTest.java | 6 +- 4 files changed, 111 insertions(+), 32 deletions(-) rename modules/utils/src/main/java/org/springside/modules/utils/base/{PropertiesUtil.java => SystemProperties.java} (99%) diff --git a/modules/utils/pom.xml b/modules/utils/pom.xml index b757135f..559df524 100644 --- a/modules/utils/pom.xml +++ b/modules/utils/pom.xml @@ -31,7 +31,7 @@ 0.9.10 - + commons-codec commons-codec @@ -39,7 +39,7 @@ true - + commons-beanutils commons-beanutils @@ -47,7 +47,7 @@ true - + com.fasterxml.jackson.core jackson-databind @@ -55,7 +55,7 @@ true - + ma.glasnost.orika orika-core diff --git a/modules/utils/src/main/java/org/springside/modules/utils/base/PropertiesUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/base/SystemProperties.java similarity index 99% rename from modules/utils/src/main/java/org/springside/modules/utils/base/PropertiesUtil.java rename to modules/utils/src/main/java/org/springside/modules/utils/base/SystemProperties.java index 01c2de9c..3e7b3268 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/base/PropertiesUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/base/SystemProperties.java @@ -9,7 +9,7 @@ package org.springside.modules.utils.base; * * @author calvin */ -public class PropertiesUtil { +public class SystemProperties { /////////// Boolean.readBoolean()扩展 /////////////// diff --git a/modules/utils/src/main/java/org/springside/modules/utils/reflect/Reflections.java b/modules/utils/src/main/java/org/springside/modules/utils/reflect/Reflections.java index 4d1bfd47..b7563970 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/reflect/Reflections.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/reflect/Reflections.java @@ -10,19 +10,27 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.Beta; - /** * 反射工具类. * - * 提供调用getter/setter方法, 访问私有变量, 调用私有方法. + * 所有反射均无视modifier的范围限制,同时将反射的Checked异常转为UnChecked异常。 + * + * 基于这两点要求,没有现有库满足,只能自写 + * + * 1. 属性访问:a.调用getter/setter方法, b.直接访问变量, c.直接先尝试查找getter/setter,如果不存在则直接访问变量 + * + * 2. 一次性调用方法,其中一个特色函数是invokeByName,不准确匹配参数类型,只取第一个名称符合的函数. + * + * 3. 多次调用方法,先用getAccessibleMethod()获得Method对象,然后多次调用Method对象 */ -@Beta +@SuppressWarnings("unchecked") public class Reflections { private static final String SETTER_PREFIX = "set"; @@ -30,26 +38,27 @@ public class Reflections { private static Logger logger = LoggerFactory.getLogger(Reflections.class); + /////////// 属性相关函数/////////// /** - * 调用Getter方法. + * 调用Getter方法,无视private/protected修饰符. */ - public static Object invokeGetter(Object obj, String propertyName) { + public static T invokeGetter(Object obj, String propertyName) { String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(propertyName); - return invokeMethod(obj, getterMethodName, new Class[] {}, new Object[] {}); + return (T) invokeMethod(obj, getterMethodName); } /** - * 调用Setter方法, 仅匹配方法名。 + * 调用Setter方法, 无视private/protected修饰符, 按传入value的类型匹配函数. */ public static void invokeSetter(Object obj, String propertyName, Object value) { String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(propertyName); - invokeMethodByName(obj, setterMethodName, new Object[] { value }); + invokeMethod(obj, setterMethodName, new Object[] { value }); } /** * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. */ - public static Object getFieldValue(final Object obj, final String fieldName) { + public static T getFieldValue(final Object obj, final String fieldName) { Field field = getAccessibleField(obj, fieldName); if (field == null) { @@ -62,7 +71,7 @@ public class Reflections { } catch (IllegalAccessException e) { logger.error("不可能抛出的异常{}", e.getMessage()); } - return result; + return (T) result; } /** @@ -83,10 +92,63 @@ public class Reflections { } /** - * 直接调用对象方法, 无视private/protected修饰符. 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. 同时匹配方法名+参数类型, + * 先尝试用Getter函数读取, 如果不存在则直接读取变量. */ - public static Object invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, - final Object[] args) { + public static T getProperty(Object obj, String propertyName) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(propertyName); + Method method = getAccessibleMethod(obj, getterMethodName); + if (method != null) { + try { + return (T) method.invoke(obj, ArrayUtils.EMPTY_OBJECT_ARRAY); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } else { + return (T) getFieldValue(obj, propertyName); + } + } + + /** + * 先尝试用Setter函数写入, 如果不存在则直接写入变量, 按传入value的类型匹配函数. + */ + public static void setProperty(Object obj, String propertyName, final Object value) { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(propertyName); + Method method = getAccessibleMethod(obj, setterMethodName, value.getClass()); + if (method != null) { + try { + method.invoke(obj, value); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } else { + setFieldValue(obj, propertyName, value); + } + } + + ////////// 方法相关函数 ////// + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * + * 根据传入参数的实际类型进行匹配 + * + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + */ + public static T invokeMethod(Object obj, String methodName, Object... args) { + args = ArrayUtils.nullToEmpty(args); + final Class[] parameterTypes = ClassUtils.toClass(args); + return (T) invokeMethod(obj, methodName, args, parameterTypes); + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * + * 根据定义的参数类型进行匹配, + * + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + */ + public static Object invokeMethod(final Object obj, final String methodName, final Object[] args, + final Class[] parameterTypes) { Method method = getAccessibleMethod(obj, methodName, parameterTypes); if (method == null) { throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); @@ -100,8 +162,11 @@ public class Reflections { } /** - * 直接调用对象方法, 无视private/protected修饰符, 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. - * 只匹配函数名,如果有多个同名函数调用第一个。 + * 直接调用对象方法, 无视private/protected修饰符 + * + * 只匹配函数名,如果有多个同名函数调用第一个. 用于确信只有一个同名函数, 但参数类型不好确定的情况 + * + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. */ public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { Method method = getAccessibleMethodByName(obj, methodName); @@ -120,10 +185,12 @@ public class Reflections { * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. * * 如向上转型到Object仍无法找到, 返回null. + * + * 因为class.getFiled(); 不能获取父类的private函数, 因此采用循环向上的getDeclaredField(); */ public static Field getAccessibleField(final Object obj, final String fieldName) { Validate.notNull(obj, "object can't be null"); - Validate.notBlank(fieldName, "fieldName can't be blank"); + Validate.notEmpty(fieldName, "fieldName can't be blank"); for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass .getSuperclass()) { try { @@ -138,14 +205,20 @@ public class Reflections { } /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null. 匹配函数名+参数类型。 + * 循环向上转型, 获取对象的DeclaredMethod, 并强制设置为可访问. * - * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + * 如向上转型到Object仍无法找到, 返回null. + * + * 匹配函数名+参数类型. + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method, 然后调用Method.invoke(Object obj, Object... args) + * + * 因为class.getFiled() 不能获取父类的private函数, 因此采用循环向上的getDeclaredField(); */ - public static Method getAccessibleMethod(final Object obj, final String methodName, - final Class... parameterTypes) { + public static Method getAccessibleMethod(final Object obj, final String methodName, Class... parameterTypes) { Validate.notNull(obj, "object can't be null"); - Validate.notBlank(methodName, "methodName can't be blank"); + Validate.notEmpty(methodName, "methodName can't be blank"); + parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType .getSuperclass()) { @@ -161,13 +234,19 @@ public class Reflections { } /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null. 只匹配函数名。 + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * + * 如向上转型到Object仍无法找到, 返回null. + * + * 只匹配函数名, 如果有多个同名函数返回第一个 * * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + * + * 因为class.getMethods() 不能获取父类的private函数, 因此采用循环向上的getMethods(); */ public static Method getAccessibleMethodByName(final Object obj, final String methodName) { Validate.notNull(obj, "object can't be null"); - Validate.notBlank(methodName, "methodName can't be blank"); + Validate.notEmpty(methodName, "methodName can't be blank"); for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType .getSuperclass()) { diff --git a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionsTest.java b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionsTest.java index a27646a3..74e019ce 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionsTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionsTest.java @@ -63,7 +63,7 @@ public class ReflectionsTest { // 使用函数名+参数类型的匹配 assertThat( Reflections - .invokeMethod(bean, "privateMethod", new Class[] { String.class }, new Object[] { "calvin" })) + .invokeMethod(bean, "privateMethod", new Object[] { "calvin" }, new Class[] { String.class })) .isEqualTo("hello calvin"); // 仅匹配函数名 @@ -72,7 +72,7 @@ public class ReflectionsTest { // 函数名错 try { - Reflections.invokeMethod(bean, "notExistMethod", new Class[] { String.class }, new Object[] { "calvin" }); + Reflections.invokeMethod(bean, "notExistMethod", new Object[] { "calvin" }, new Class[] { String.class }); failBecauseExceptionWasNotThrown(IllegalArgumentException.class); } catch (IllegalArgumentException e) { @@ -80,7 +80,7 @@ public class ReflectionsTest { // 参数类型错 try { - Reflections.invokeMethod(bean, "privateMethod", new Class[] { Integer.class }, new Object[] { "calvin" }); + Reflections.invokeMethod(bean, "privateMethod", new Object[] { "calvin" }, new Class[] { Integer.class }); failBecauseExceptionWasNotThrown(RuntimeException.class); } catch (RuntimeException e) {