From 6d7d350886406d95c775d40f6afdd46fd6ddade5 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 5 May 2022 00:24:08 +0800 Subject: [PATCH] add MethodUtil --- .../core/annotation/AnnotationProxy.java | 6 +- .../core/annotation/AnnotationUtil.java | 9 +- .../java/cn/hutool/core/bean/BeanDesc.java | 3 +- .../java/cn/hutool/core/bean/DynaBean.java | 3 +- .../java/cn/hutool/core/bean/PropDesc.java | 10 +- .../core/classloader/ClassLoaderUtil.java | 11 +- .../core/classloader/JarClassLoader.java | 6 +- .../hutool/core/clone/DefaultCloneable.java | 4 +- .../hutool/core/collection/iter/IterUtil.java | 3 +- .../java/cn/hutool/core/convert/Convert.java | 8 +- .../core/convert/impl/EnumConverter.java | 6 +- .../hutool/core/io/resource/VfsResource.java | 16 +- .../java/cn/hutool/core/lang/Singleton.java | 6 +- .../cn/hutool/core/lang/func/LambdaUtil.java | 10 +- .../cn/hutool/core/reflect/ClassUtil.java | 189 ----- .../hutool/core/reflect/MethodHandleUtil.java | 4 +- .../cn/hutool/core/reflect/MethodUtil.java | 734 ++++++++++++++++++ .../cn/hutool/core/reflect/ReflectUtil.java | 625 --------------- .../java/cn/hutool/core/regex/ReUtil.java | 6 +- .../java/cn/hutool/core/util/ObjUtil.java | 6 +- .../core/reflect/MethodHandleUtilTest.java | 14 +- .../hutool/core/reflect/MethodUtilTest.java | 169 ++++ .../hutool/core/reflect/ReflectUtilTest.java | 178 +++++ .../cn/hutool/core/util/ReflectUtilTest.java | 341 -------- .../cn/hutool/core/util/TypeUtilTest.java | 5 +- .../java/cn/hutool/cron/task/InvokeTask.java | 5 +- .../src/main/java/cn/hutool/db/Entity.java | 8 +- .../cn/hutool/db/handler/ResultSetUtil.java | 3 +- .../extra/aop/interceptor/JdkInterceptor.java | 6 +- .../poi/excel/sax/Excel07SaxReader.java | 8 +- 30 files changed, 1168 insertions(+), 1234 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/reflect/MethodUtil.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/reflect/MethodUtilTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/reflect/ReflectUtilTest.java delete mode 100755 hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java index 4e08ba565..cb89e043a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java @@ -1,6 +1,6 @@ package cn.hutool.core.annotation; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; import java.io.Serializable; @@ -71,7 +71,7 @@ public class AnnotationProxy implements Annotation, Invoca * @return 属性(方法结果)映射 */ private Map initAttributes() { - final Method[] methods = ReflectUtil.getMethods(this.type); + final Method[] methods = MethodUtil.getMethods(this.type); final Map attributes = new HashMap<>(methods.length, 1); for (final Method method : methods) { @@ -80,7 +80,7 @@ public class AnnotationProxy implements Annotation, Invoca continue; } - attributes.put(method.getName(), ReflectUtil.invoke(this.annotation, method)); + attributes.put(method.getName(), MethodUtil.invoke(this.annotation, method)); } return attributes; diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java index 659d8dcab..884e98173 100755 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java @@ -1,6 +1,7 @@ package cn.hutool.core.annotation; import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.reflect.ReflectUtil; @@ -169,11 +170,11 @@ public class AnnotationUtil { return null; } - final Method method = ReflectUtil.getMethodOfObj(annotation, propertyName); + final Method method = MethodUtil.getMethodOfObj(annotation, propertyName); if (null == method) { return null; } - return ReflectUtil.invoke(annotation, method); + return MethodUtil.invoke(annotation, method); } /** @@ -191,7 +192,7 @@ public class AnnotationUtil { return null; } - final Method[] methods = ReflectUtil.getMethods(annotationType, t -> { + final Method[] methods = MethodUtil.getMethods(annotationType, t -> { if (ArrayUtil.isEmpty(t.getParameterTypes())) { // 只读取无参方法 final String name = t.getName(); @@ -205,7 +206,7 @@ public class AnnotationUtil { final HashMap result = new HashMap<>(methods.length, 1); for (final Method method : methods) { - result.put(method.getName(), ReflectUtil.invoke(annotation, method)); + result.put(method.getName(), MethodUtil.invoke(annotation, method)); } return result; } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java index efc412fbe..161f27abd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java @@ -2,6 +2,7 @@ package cn.hutool.core.bean; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.CaseInsensitiveMap; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.reflect.ModifierUtil; import cn.hutool.core.reflect.ReflectUtil; @@ -140,7 +141,7 @@ public class BeanDesc implements Serializable { * @return this */ private BeanDesc init() { - final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase); + final Method[] gettersAndSetters = MethodUtil.getMethods(this.beanClass, MethodUtil::isGetterOrSetterIgnoreCase); PropDesc prop; for (final Field field : ReflectUtil.getFields(this.beanClass)) { // 排除静态属性和对象子类 diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java b/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java index be89ccd0a..8a8991b58 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java @@ -3,6 +3,7 @@ package cn.hutool.core.bean; import cn.hutool.core.clone.CloneSupport; import cn.hutool.core.lang.Assert; import cn.hutool.core.reflect.ClassUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.ReflectUtil; import java.io.Serializable; @@ -169,7 +170,7 @@ public class DynaBean extends CloneSupport implements Serializable { * @return 执行结果,可能为null */ public Object invoke(final String methodName, final Object... params) { - return ReflectUtil.invoke(this.bean, methodName, params); + return MethodUtil.invoke(this.bean, methodName, params); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java index adc04112d..f320e2c12 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java @@ -3,7 +3,7 @@ package cn.hutool.core.bean; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.PropIgnore; import cn.hutool.core.convert.Convert; -import cn.hutool.core.reflect.ClassUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.ModifierUtil; import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.reflect.TypeUtil; @@ -43,8 +43,8 @@ public class PropDesc { */ public PropDesc(final Field field, final Method getter, final Method setter) { this.field = field; - this.getter = ClassUtil.setAccessible(getter); - this.setter = ClassUtil.setAccessible(setter); + this.getter = ReflectUtil.setAccessible(getter); + this.setter = ReflectUtil.setAccessible(setter); } /** @@ -152,7 +152,7 @@ public class PropDesc { */ public Object getValue(final Object bean) { if (null != this.getter) { - return ReflectUtil.invoke(bean, this.getter); + return MethodUtil.invoke(bean, this.getter); } else if (ModifierUtil.isPublic(this.field)) { return ReflectUtil.getFieldValue(bean, this.field); } @@ -223,7 +223,7 @@ public class PropDesc { */ public PropDesc setValue(final Object bean, final Object value) { if (null != this.setter) { - ReflectUtil.invoke(bean, this.setter, value); + MethodUtil.invoke(bean, this.setter, value); } else if (ModifierUtil.isPublic(this.field)) { ReflectUtil.setFieldValue(bean, this.field, value); } diff --git a/hutool-core/src/main/java/cn/hutool/core/classloader/ClassLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/classloader/ClassLoaderUtil.java index f6319086e..3cd047857 100644 --- a/hutool-core/src/main/java/cn/hutool/core/classloader/ClassLoaderUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/classloader/ClassLoaderUtil.java @@ -140,11 +140,12 @@ public class ClassLoaderUtil { * 3、内部类,例如:java.lang.Thread.State会被转为java.lang.Thread$State加载 * * + * @param 目标类的类型 * @param name 类名 * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 */ - public static Class loadClass(final String name) throws UtilException { + public static Class loadClass(final String name) throws UtilException { return loadClass(name, true); } @@ -158,12 +159,13 @@ public class ClassLoaderUtil { * 3、内部类,例如:java.lang.Thread.State会被转为java.lang.Thread$State加载 * * + * @param 目标类的类型 * @param name 类名 * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性) * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 */ - public static Class loadClass(final String name, final boolean isInitialized) throws UtilException { + public static Class loadClass(final String name, final boolean isInitialized) throws UtilException { return loadClass(name, null, isInitialized); } @@ -185,7 +187,8 @@ public class ClassLoaderUtil { * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 */ - public static Class loadClass(String name, ClassLoader classLoader, final boolean isInitialized) throws UtilException { + @SuppressWarnings("unchecked") + public static Class loadClass(String name, ClassLoader classLoader, final boolean isInitialized) throws UtilException { Assert.notNull(name, "Name must not be null"); // 自动将包名中的"/"替换为"." @@ -201,7 +204,7 @@ public class ClassLoaderUtil { final ClassLoader finalClassLoader = classLoader; clazz = CLASS_CACHE.computeIfAbsent(Pair.of(name, classLoader), (key)-> doLoadClass(finalName, finalClassLoader, isInitialized)); } - return clazz; + return (Class) clazz; } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/classloader/JarClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/classloader/JarClassLoader.java index 86e4cc456..e6a80e82b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/classloader/JarClassLoader.java +++ b/hutool-core/src/main/java/cn/hutool/core/classloader/JarClassLoader.java @@ -3,7 +3,7 @@ package cn.hutool.core.classloader; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.io.FileUtil; import cn.hutool.core.net.URLUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import java.io.File; import java.io.IOException; @@ -53,12 +53,12 @@ public class JarClassLoader extends URLClassLoader { */ public static void loadJar(final URLClassLoader loader, final File jarFile) throws UtilException { try { - final Method method = ReflectUtil.getMethod(URLClassLoader.class, "addURL", URL.class); + final Method method = MethodUtil.getMethod(URLClassLoader.class, "addURL", URL.class); if (null != method) { method.setAccessible(true); final List jars = loopJar(jarFile); for (final File jar : jars) { - ReflectUtil.invoke(loader, method, jar.toURI().toURL()); + MethodUtil.invoke(loader, method, jar.toURI().toURL()); } } } catch (final IOException e) { diff --git a/hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java b/hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java index 62fce46c0..f58632711 100644 --- a/hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java +++ b/hutool-core/src/main/java/cn/hutool/core/clone/DefaultCloneable.java @@ -1,7 +1,7 @@ package cn.hutool.core.clone; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; /** * 克隆默认实现接口,用于实现返回指定泛型类型的克隆方法 @@ -18,7 +18,7 @@ public interface DefaultCloneable extends java.lang.Cloneable { */ default T clone0() { try { - return ReflectUtil.invoke(this, "clone"); + return MethodUtil.invoke(this, "clone"); } catch (final Exception e) { throw new CloneRuntimeException(e); } diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/iter/IterUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/iter/IterUtil.java index bcb5466a6..2264783c1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/iter/IterUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/iter/IterUtil.java @@ -8,6 +8,7 @@ import cn.hutool.core.lang.func.Filter; import cn.hutool.core.lang.func.Matcher; import cn.hutool.core.lang.func.Func1; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrJoiner; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; @@ -1046,7 +1047,7 @@ public class IterUtil { // 反射获取 try { - final Object iterator = ReflectUtil.invoke(obj, "iterator"); + final Object iterator = MethodUtil.invoke(obj, "iterator"); if (iterator instanceof Iterator) { return (Iterator) iterator; } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java index 245579a0c..1f95b2b8a 100755 --- a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java @@ -1,17 +1,17 @@ package cn.hutool.core.convert; +import cn.hutool.core.classloader.ClassLoaderUtil; +import cn.hutool.core.codec.HexUtil; import cn.hutool.core.convert.impl.CollectionConverter; import cn.hutool.core.convert.impl.EnumConverter; import cn.hutool.core.convert.impl.MapConverter; import cn.hutool.core.lang.Assert; import cn.hutool.core.reflect.TypeReference; +import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.UnicodeUtil; import cn.hutool.core.util.ByteUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.reflect.ClassUtil; -import cn.hutool.core.codec.HexUtil; -import cn.hutool.core.text.StrUtil; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -627,7 +627,7 @@ public class Convert { * @throws ConvertException 转换器不存在 */ public static T convertByClassName(final String className, final Object value) throws ConvertException{ - return convert(ClassUtil.loadClass(className), value); + return convert(ClassLoaderUtil.loadClass(className), value); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index ebe81e4fb..4f0dc4dad 100755 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java @@ -6,9 +6,9 @@ import cn.hutool.core.lang.EnumItem; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.reflect.ClassUtil; -import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.ModifierUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.util.EnumUtil; import java.lang.reflect.Method; import java.util.Arrays; @@ -96,7 +96,7 @@ public class EnumConverter extends AbstractConverter { final Class valueClass = value.getClass(); for (final Map.Entry, Method> entry : methodMap.entrySet()) { if (ClassUtil.isAssignable(entry.getKey(), valueClass)) { - return ReflectUtil.invokeStatic(entry.getValue(), value); + return MethodUtil.invokeStatic(entry.getValue(), value); } } } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/VfsResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/VfsResource.java index 5936005a6..de047f7c8 100755 --- a/hutool-core/src/main/java/cn/hutool/core/io/resource/VfsResource.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/VfsResource.java @@ -1,8 +1,8 @@ package cn.hutool.core.io.resource; -import cn.hutool.core.lang.Assert; import cn.hutool.core.classloader.ClassLoaderUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.reflect.MethodUtil; import java.io.InputStream; import java.lang.reflect.Method; @@ -63,22 +63,22 @@ public class VfsResource implements Resource { * @return 文件是否存在 */ public boolean exists() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_EXISTS); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_EXISTS); } @Override public String getName() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_NAME); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_NAME); } @Override public URL getUrl() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_TO_URL); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_TO_URL); } @Override public InputStream getStream() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_INPUT_STREAM); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_INPUT_STREAM); } @Override @@ -92,7 +92,7 @@ public class VfsResource implements Resource { * @return 最后修改时间 */ public long getLastModified() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED); } /** @@ -101,7 +101,7 @@ public class VfsResource implements Resource { * @return VFS文件大小 */ public long size() { - return ReflectUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_SIZE); + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_SIZE); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java b/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java index 1d52b6534..1b9f4e4a3 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Singleton.java @@ -1,10 +1,10 @@ package cn.hutool.core.lang; +import cn.hutool.core.classloader.ClassLoaderUtil; import cn.hutool.core.lang.func.Func0; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.reflect.ClassUtil; import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ArrayUtil; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -66,7 +66,7 @@ public final class Singleton { */ public static T get(final String className, final Object... params) { Assert.notBlank(className, "Class name must be not blank !"); - final Class clazz = ClassUtil.loadClass(className); + final Class clazz = ClassLoaderUtil.loadClass(className); return get(clazz, params); } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java index cd7a754f0..df348b6a6 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java @@ -1,9 +1,9 @@ package cn.hutool.core.lang.func; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.classloader.ClassLoaderUtil; import cn.hutool.core.map.WeakConcurrentMap; -import cn.hutool.core.reflect.ClassUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; import java.io.Serializable; @@ -57,7 +57,7 @@ public class LambdaUtil { public static Class getRealClass(final Func0 func) { final SerializedLambda lambda = resolve(func); checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); - return ClassUtil.loadClass(lambda.getImplClass()); + return ClassLoaderUtil.loadClass(lambda.getImplClass()); } /** @@ -134,7 +134,7 @@ public class LambdaUtil { final SerializedLambda lambda = resolve(func); checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); final String instantiatedMethodType = lambda.getInstantiatedMethodType(); - return ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); + return ClassLoaderUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); } /** @@ -202,7 +202,7 @@ public class LambdaUtil { * @return 返回解析后的结果 */ private static SerializedLambda _resolve(final Serializable func) { - return cache.computeIfAbsent(func.getClass().getName(), (key) -> ReflectUtil.invoke(func, "writeReplace")); + return cache.computeIfAbsent(func.getClass().getName(), (key) -> MethodUtil.invoke(func, "writeReplace")); } //endregion } diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/ClassUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/ClassUtil.java index 91b391600..42c041ac1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/ClassUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/ClassUtil.java @@ -6,9 +6,7 @@ import cn.hutool.core.convert.BasicType; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.ClassScanner; -import cn.hutool.core.lang.Singleton; import cn.hutool.core.net.URLDecoder; import cn.hutool.core.net.URLUtil; import cn.hutool.core.text.StrUtil; @@ -18,7 +16,6 @@ import cn.hutool.core.util.CharsetUtil; import java.io.IOException; import java.lang.annotation.Annotation; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URI; @@ -358,124 +355,6 @@ public class ClassUtil { return true; } - /** - * 加载类 - * - * @param 对象类型 - * @param className 类名 - * @param isInitialized 是否初始化 - * @return 类 - */ - @SuppressWarnings("unchecked") - public static Class loadClass(final String className, final boolean isInitialized) { - return (Class) ClassLoaderUtil.loadClass(className, isInitialized); - } - - /** - * 加载类并初始化 - * - * @param 对象类型 - * @param className 类名 - * @return 类 - */ - public static Class loadClass(final String className) { - return loadClass(className, true); - } - - // ---------------------------------------------------------------------------------------------------- Invoke start - - /** - * 执行方法
- * 可执行Private方法,也可执行static方法
- * 执行非static方法时,必须满足对象有默认构造方法
- * 非单例模式,如果是非静态方法,每次创建一个新对象 - * - * @param 对象类型 - * @param classNameWithMethodName 类名和方法名表达式,类名与方法名用{@code .}或{@code #}连接 例如:com.xiaoleilu.hutool.StrUtil.isEmpty 或 com.xiaoleilu.hutool.StrUtil#isEmpty - * @param args 参数,必须严格对应指定方法的参数类型和数量 - * @return 返回结果 - */ - public static T invoke(final String classNameWithMethodName, final Object[] args) { - return invoke(classNameWithMethodName, false, args); - } - - /** - * 执行方法
- * 可执行Private方法,也可执行static方法
- * 执行非static方法时,必须满足对象有默认构造方法
- * - * @param 对象类型 - * @param classNameWithMethodName 类名和方法名表达式,例如:com.xiaoleilu.hutool.StrUtil#isEmpty或com.xiaoleilu.hutool.StrUtil.isEmpty - * @param isSingleton 是否为单例对象,如果此参数为false,每次执行方法时创建一个新对象 - * @param args 参数,必须严格对应指定方法的参数类型和数量 - * @return 返回结果 - */ - public static T invoke(final String classNameWithMethodName, final boolean isSingleton, final Object... args) { - if (StrUtil.isBlank(classNameWithMethodName)) { - throw new UtilException("Blank classNameDotMethodName!"); - } - - int splitIndex = classNameWithMethodName.lastIndexOf('#'); - if (splitIndex <= 0) { - splitIndex = classNameWithMethodName.lastIndexOf('.'); - } - if (splitIndex <= 0) { - throw new UtilException("Invalid classNameWithMethodName [{}]!", classNameWithMethodName); - } - - final String className = classNameWithMethodName.substring(0, splitIndex); - final String methodName = classNameWithMethodName.substring(splitIndex + 1); - - return invoke(className, methodName, isSingleton, args); - } - - /** - * 执行方法
- * 可执行Private方法,也可执行static方法
- * 执行非static方法时,必须满足对象有默认构造方法
- * 非单例模式,如果是非静态方法,每次创建一个新对象 - * - * @param 对象类型 - * @param className 类名,完整类路径 - * @param methodName 方法名 - * @param args 参数,必须严格对应指定方法的参数类型和数量 - * @return 返回结果 - */ - public static T invoke(final String className, final String methodName, final Object[] args) { - return invoke(className, methodName, false, args); - } - - /** - * 执行方法
- * 可执行Private方法,也可执行static方法
- * 执行非static方法时,必须满足对象有默认构造方法
- * - * @param 对象类型 - * @param className 类名,完整类路径 - * @param methodName 方法名 - * @param isSingleton 是否为单例对象,如果此参数为false,每次执行方法时创建一个新对象 - * @param args 参数,必须严格对应指定方法的参数类型和数量 - * @return 返回结果 - */ - public static T invoke(final String className, final String methodName, final boolean isSingleton, final Object... args) { - final Class clazz = loadClass(className); - try { - final Method method = ReflectUtil.getMethod(clazz, methodName, getClasses(args)); - if (null == method) { - throw new NoSuchMethodException(StrUtil.format("No such method: [{}]", methodName)); - } - if (isStatic(method)) { - return ReflectUtil.invoke(null, method, args); - } else { - return ReflectUtil.invoke(isSingleton ? Singleton.get(clazz) : clazz.newInstance(), method, args); - } - } catch (final Exception e) { - throw new UtilException(e); - } - } - - // ---------------------------------------------------------------------------------------------------- Invoke end - /** * 是否为包装类型 * @@ -580,74 +459,6 @@ public class ClassUtil { } } - /** - * 指定类是否为Public - * - * @param clazz 类 - * @return 是否为public - */ - public static boolean isPublic(final Class clazz) { - if (null == clazz) { - throw new NullPointerException("Class to provided is null."); - } - return Modifier.isPublic(clazz.getModifiers()); - } - - /** - * 指定方法是否为Public - * - * @param method 方法 - * @return 是否为public - */ - public static boolean isPublic(final Method method) { - Assert.notNull(method, "Method to provided is null."); - return Modifier.isPublic(method.getModifiers()); - } - - /** - * 指定类是否为非public - * - * @param clazz 类 - * @return 是否为非public - */ - public static boolean isNotPublic(final Class clazz) { - return false == isPublic(clazz); - } - - /** - * 指定方法是否为非public - * - * @param method 方法 - * @return 是否为非public - */ - public static boolean isNotPublic(final Method method) { - return false == isPublic(method); - } - - /** - * 是否为静态方法 - * - * @param method 方法 - * @return 是否为静态方法 - */ - public static boolean isStatic(final Method method) { - Assert.notNull(method, "Method to provided is null."); - return Modifier.isStatic(method.getModifiers()); - } - - /** - * 设置方法为可访问 - * - * @param method 方法 - * @return 方法 - */ - public static Method setAccessible(final Method method) { - if (null != method && false == method.isAccessible()) { - method.setAccessible(true); - } - return method; - } - /** * 是否为抽象类 * diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java index 6402eef98..063fab56b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java @@ -14,7 +14,7 @@ import java.lang.reflect.Method; * 方法句柄是一个有类型的,可以直接执行的指向底层方法、构造器、field等的引用,可以简单理解为函数指针,它是一种更加底层的查找、调整和调用方法的机制。 * 参考: * * * @author looly @@ -138,7 +138,7 @@ public class MethodHandleUtil { Assert.notNull(obj, "Object to get method must be not null!"); Assert.notBlank(methodName, "Method name must be not blank!"); - final Method method = ReflectUtil.getMethodOfObj(obj, methodName, args); + final Method method = MethodUtil.getMethodOfObj(obj, methodName, args); if (null == method) { throw new UtilException("No such method: [{}] from [{}]", methodName, obj.getClass()); } diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/MethodUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodUtil.java new file mode 100644 index 000000000..bbcbd5312 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodUtil.java @@ -0,0 +1,734 @@ +package cn.hutool.core.reflect; + +import cn.hutool.core.bean.NullWrapperBean; +import cn.hutool.core.classloader.ClassLoaderUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.UniqueKeySet; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Singleton; +import cn.hutool.core.lang.func.Filter; +import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ArrayUtil; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MethodUtil { + /** + * 方法缓存 + */ + private static final WeakConcurrentMap, Method[]> METHODS_CACHE = new WeakConcurrentMap<>(); + + // --------------------------------------------------------------------------------------------------------- method + + /** + * 获得指定类本类及其父类中的Public方法名
+ * 去重重载的方法 + * + * @param clazz 类 + * @return 方法名Set + */ + public static Set getPublicMethodNames(final Class clazz) { + final HashSet methodSet = new HashSet<>(); + final Method[] methodArray = getPublicMethods(clazz); + if (ArrayUtil.isNotEmpty(methodArray)) { + for (final Method method : methodArray) { + methodSet.add(method.getName()); + } + } + return methodSet; + } + + /** + * 获得本类及其父类所有Public方法 + * + * @param clazz 查找方法的类 + * @return 过滤后的方法列表 + */ + public static Method[] getPublicMethods(final Class clazz) { + return null == clazz ? null : clazz.getMethods(); + } + + /** + * 获得指定类过滤后的Public方法列表
+ * + * @param clazz 查找方法的类 + * @param filter 过滤器 + * @return 过滤后的方法数组 + */ + public static Method[] getPublicMethods(final Class clazz, final Filter filter) { + if (null == clazz) { + return null; + } + + final Method[] methods = getPublicMethods(clazz); + if(null == filter){ + return methods; + } + + return ArrayUtil.filter(methods, filter); + } + + /** + * 获得指定类过滤后的Public方法列表 + * + * @param clazz 查找方法的类 + * @param excludeMethods 不包括的方法 + * @return 过滤后的方法列表 + */ + public static Method[] getPublicMethods(final Class clazz, final Method... excludeMethods) { + final HashSet excludeMethodSet = CollUtil.newHashSet(excludeMethods); + return getPublicMethods(clazz, method -> false == excludeMethodSet.contains(method)); + } + + /** + * 获得指定类过滤后的Public方法列表 + * + * @param clazz 查找方法的类 + * @param excludeMethodNames 不包括的方法名列表 + * @return 过滤后的方法数组 + */ + public static Method[] getPublicMethods(final Class clazz, final String... excludeMethodNames) { + final HashSet excludeMethodNameSet = CollUtil.newHashSet(excludeMethodNames); + return getPublicMethods(clazz, method -> false == excludeMethodNameSet.contains(method.getName())); + } + + /** + * 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回{@code null} + * + * @param clazz 类 + * @param methodName 方法名 + * @param paramTypes 参数类型 + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + */ + public static Method getPublicMethod(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { + try { + return clazz.getMethod(methodName, paramTypes); + } catch (final NoSuchMethodException ex) { + return null; + } + } + + /** + * 查找指定对象中的所有方法(包括非public方法),也包括父对象和Object类的方法 + * + *

+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 + *

+ * + * @param obj 被查找的对象,如果为{@code null}返回{@code null} + * @param methodName 方法名,如果为空字符串返回{@code null} + * @param args 参数 + * @return 方法 + * @throws SecurityException 无访问权限抛出异常 + */ + public static Method getMethodOfObj(final Object obj, final String methodName, final Object... args) throws SecurityException { + if (null == obj || StrUtil.isBlank(methodName)) { + return null; + } + return getMethod(obj.getClass(), methodName, ClassUtil.getClasses(args)); + } + + /** + * 忽略大小写查找指定方法,如果找不到对应的方法则返回{@code null} + * + *

+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 + *

+ * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param methodName 方法名,如果为空字符串返回{@code null} + * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 3.2.0 + */ + public static Method getMethodIgnoreCase(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { + return getMethod(clazz, true, methodName, paramTypes); + } + + /** + * 查找指定方法 如果找不到对应的方法则返回{@code null} + * + *

+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 + *

+ * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param methodName 方法名,如果为空字符串返回{@code null} + * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + */ + public static Method getMethod(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { + return getMethod(clazz, false, methodName, paramTypes); + } + + /** + * 查找指定方法 如果找不到对应的方法则返回{@code null}
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
+ * 如果查找的方法有多个同参数类型重载,查找第一个找到的方法 + * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param ignoreCase 是否忽略大小写 + * @param methodName 方法名,如果为空字符串返回{@code null} + * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 3.2.0 + */ + public static Method getMethod(final Class clazz, final boolean ignoreCase, final String methodName, final Class... paramTypes) throws SecurityException { + if (null == clazz || StrUtil.isBlank(methodName)) { + return null; + } + + final Method[] methods = getMethods(clazz); + if (ArrayUtil.isNotEmpty(methods)) { + for (final Method method : methods) { + if (StrUtil.equals(methodName, method.getName(), ignoreCase) + && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) + //排除桥接方法,pr#1965@Github + && false == method.isBridge()) { + return method; + } + } + } + return null; + } + + /** + * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} + * + *

+ * 此方法只检查方法名是否一致,并不检查参数的一致性。 + *

+ * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param methodName 方法名,如果为空字符串返回{@code null} + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 4.3.2 + */ + public static Method getMethodByName(final Class clazz, final String methodName) throws SecurityException { + return getMethodByName(clazz, false, methodName); + } + + /** + * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} + * + *

+ * 此方法只检查方法名是否一致(忽略大小写),并不检查参数的一致性。 + *

+ * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param methodName 方法名,如果为空字符串返回{@code null} + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 4.3.2 + */ + public static Method getMethodByNameIgnoreCase(final Class clazz, final String methodName) throws SecurityException { + return getMethodByName(clazz, true, methodName); + } + + /** + * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} + * + *

+ * 此方法只检查方法名是否一致,并不检查参数的一致性。 + *

+ * + * @param clazz 类,如果为{@code null}返回{@code null} + * @param ignoreCase 是否忽略大小写 + * @param methodName 方法名,如果为空字符串返回{@code null} + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 4.3.2 + */ + public static Method getMethodByName(final Class clazz, final boolean ignoreCase, final String methodName) throws SecurityException { + if (null == clazz || StrUtil.isBlank(methodName)) { + return null; + } + + final Method[] methods = getMethods(clazz); + if (ArrayUtil.isNotEmpty(methods)) { + for (final Method method : methods) { + if (StrUtil.equals(methodName, method.getName(), ignoreCase) + // 排除桥接方法 + && false == method.isBridge()) { + return method; + } + } + } + return null; + } + + /** + * 获得指定类中的Public方法名
+ * 去重重载的方法 + * + * @param clazz 类 + * @return 方法名Set + * @throws SecurityException 安全异常 + */ + public static Set getMethodNames(final Class clazz) throws SecurityException { + final HashSet methodSet = new HashSet<>(); + final Method[] methods = getMethods(clazz); + for (final Method method : methods) { + methodSet.add(method.getName()); + } + return methodSet; + } + + /** + * 获得指定类过滤后的Public方法列表 + * + * @param clazz 查找方法的类 + * @param filter 过滤器 + * @return 过滤后的方法列表 + * @throws SecurityException 安全异常 + */ + public static Method[] getMethods(final Class clazz, final Filter filter) throws SecurityException { + if (null == clazz) { + return null; + } + return ArrayUtil.filter(getMethods(clazz), filter); + } + + /** + * 获得一个类中所有方法列表,包括其父类中的方法 + * + * @param beanClass 类,非{@code null} + * @return 方法列表 + * @throws SecurityException 安全检查异常 + */ + public static Method[] getMethods(final Class beanClass) throws SecurityException { + Assert.notNull(beanClass); + return METHODS_CACHE.computeIfAbsent(beanClass, + () -> getMethodsDirectly(beanClass, true, true)); + } + + /** + * 获得一个类中所有方法列表,直接反射获取,无缓存
+ * 接口获取方法和默认方法,获取的方法包括: + *
    + *
  • 本类中的所有方法(包括static方法)
  • + *
  • 父类中的所有方法(包括static方法)
  • + *
  • Object中(包括static方法)
  • + *
+ * + * @param beanClass 类或接口 + * @param withSupers 是否包括父类或接口的方法列表 + * @param withMethodFromObject 是否包括Object中的方法 + * @return 方法列表 + * @throws SecurityException 安全检查异常 + */ + public static Method[] getMethodsDirectly(final Class beanClass, final boolean withSupers, final boolean withMethodFromObject) throws SecurityException { + Assert.notNull(beanClass); + + if (beanClass.isInterface()) { + // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 + return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods(); + } + + final UniqueKeySet result = new UniqueKeySet<>(true, MethodUtil::getUniqueKey); + Class searchType = beanClass; + while (searchType != null) { + if (false == withMethodFromObject && Object.class == searchType) { + break; + } + result.addAllIfAbsent(Arrays.asList(searchType.getDeclaredMethods())); + result.addAllIfAbsent(getDefaultMethodsFromInterface(searchType)); + + + searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null; + } + + return result.toArray(new Method[0]); + } + + /** + * 是否为equals方法 + * + * @param method 方法 + * @return 是否为equals方法 + */ + public static boolean isEqualsMethod(final Method method) { + if (method == null || + 1 != method.getParameterCount() || + false == "equals".equals(method.getName())) { + return false; + } + return (method.getParameterTypes()[0] == Object.class); + } + + /** + * 是否为hashCode方法 + * + * @param method 方法 + * @return 是否为hashCode方法 + */ + public static boolean isHashCodeMethod(final Method method) { + return method != null// + && "hashCode".equals(method.getName())// + && isEmptyParam(method); + } + + /** + * 是否为toString方法 + * + * @param method 方法 + * @return 是否为toString方法 + */ + public static boolean isToStringMethod(final Method method) { + return method != null// + && "toString".equals(method.getName())// + && isEmptyParam(method); + } + + /** + * 是否为无参数方法 + * + * @param method 方法 + * @return 是否为无参数方法 + * @since 5.1.1 + */ + public static boolean isEmptyParam(final Method method) { + return method.getParameterCount() == 0; + } + + /** + * 检查给定方法是否为Getter或者Setter方法,规则为:
+ *
    + *
  • 方法参数必须为0个或1个
  • + *
  • 如果是无参方法,则判断是否以“get”或“is”开头
  • + *
  • 如果方法参数1个,则判断是否以“set”开头
  • + *
+ * + * @param method 方法 + * @return 是否为Getter或者Setter方法 + * @since 5.7.20 + */ + public static boolean isGetterOrSetterIgnoreCase(final Method method) { + return isGetterOrSetter(method, true); + } + + /** + * 检查给定方法是否为Getter或者Setter方法,规则为:
+ *
    + *
  • 方法参数必须为0个或1个
  • + *
  • 方法名称不能是getClass
  • + *
  • 如果是无参方法,则判断是否以“get”或“is”开头
  • + *
  • 如果方法参数1个,则判断是否以“set”开头
  • + *
+ * + * @param method 方法 + * @param ignoreCase 是否忽略方法名的大小写 + * @return 是否为Getter或者Setter方法 + * @since 5.7.20 + */ + public static boolean isGetterOrSetter(final Method method, final boolean ignoreCase) { + if (null == method) { + return false; + } + + // 参数个数必须为0或1 + final int parameterCount = method.getParameterCount(); + if (parameterCount > 1) { + return false; + } + + String name = method.getName(); + // 跳过getClass这个特殊方法 + if ("getClass".equals(name)) { + return false; + } + if (ignoreCase) { + name = name.toLowerCase(); + } + switch (parameterCount) { + case 0: + return name.startsWith("get") || name.startsWith("is"); + case 1: + return name.startsWith("set"); + default: + return false; + } + } + + // --------------------------------------------------------------------------------------------------------- invoke + + /** + * 执行静态方法 + * + * @param 对象类型 + * @param method 方法(对象方法或static方法都可) + * @param args 参数对象 + * @return 结果 + * @throws UtilException 多种异常包装 + */ + public static T invokeStatic(final Method method, final Object... args) throws UtilException { + return invoke(null, method, args); + } + + /** + * 执行方法
+ * 执行前要检查给定参数: + * + *
+	 * 1. 参数个数是否与方法参数个数一致
+	 * 2. 如果某个参数为null但是方法这个位置的参数为原始类型,则赋予原始类型默认值
+	 * 
+ * + * @param 返回对象类型 + * @param obj 对象,如果执行静态方法,此值为{@code null} + * @param method 方法(对象方法或static方法都可) + * @param args 参数对象 + * @return 结果 + * @throws UtilException 一些列异常的包装 + */ + public static T invokeWithCheck(final Object obj, final Method method, final Object... args) throws UtilException { + final Class[] types = method.getParameterTypes(); + if (null != args) { + Assert.isTrue(args.length == types.length, "Params length [{}] is not fit for param length [{}] of method !", args.length, types.length); + Class type; + for (int i = 0; i < args.length; i++) { + type = types[i]; + if (type.isPrimitive() && null == args[i]) { + // 参数是原始类型,而传入参数为null时赋予默认值 + args[i] = ClassUtil.getDefaultValue(type); + } + } + } + + return invoke(obj, method, args); + } + + /** + * 执行方法 + * + *

+ * 对于用户传入参数会做必要检查,包括: + * + *

+	 *     1、忽略多余的参数
+	 *     2、参数不够补齐默认值
+	 *     3、传入参数为null,但是目标参数类型为原始类型,做转换
+	 * 
+ * + * @param 返回对象类型 + * @param obj 对象,如果执行静态方法,此值为{@code null} + * @param method 方法(对象方法或static方法都可) + * @param args 参数对象 + * @return 结果 + * @throws UtilException 一些列异常的包装 + */ + @SuppressWarnings("unchecked") + public static T invoke(final Object obj, final Method method, final Object... args) throws UtilException { + ReflectUtil.setAccessible(method); + + // 检查用户传入参数: + // 1、忽略多余的参数 + // 2、参数不够补齐默认值 + // 3、通过NullWrapperBean传递的参数,会直接赋值null + // 4、传入参数为null,但是目标参数类型为原始类型,做转换 + // 5、传入参数类型不对应,尝试转换类型 + final Class[] parameterTypes = method.getParameterTypes(); + final Object[] actualArgs = new Object[parameterTypes.length]; + if (null != args) { + for (int i = 0; i < actualArgs.length; i++) { + if (i >= args.length || null == args[i]) { + // 越界或者空值 + actualArgs[i] = ClassUtil.getDefaultValue(parameterTypes[i]); + } else if (args[i] instanceof NullWrapperBean) { + //如果是通过NullWrapperBean传递的null参数,直接赋值null + actualArgs[i] = null; + } else if (false == parameterTypes[i].isAssignableFrom(args[i].getClass())) { + //对于类型不同的字段,尝试转换,转换失败则使用原对象类型 + final Object targetValue = Convert.convert(parameterTypes[i], args[i]); + if (null != targetValue) { + actualArgs[i] = targetValue; + } + } else { + actualArgs[i] = args[i]; + } + } + } + + if (method.isDefault()) { + // 当方法是default方法时,尤其对象是代理对象,需使用句柄方式执行 + // 代理对象情况下调用method.invoke会导致循环引用执行,最终栈溢出 + return MethodHandleUtil.invokeSpecial(obj, method, args); + } + + try { + return (T) method.invoke(ModifierUtil.isStatic(method) ? null : obj, actualArgs); + } catch (final Exception e) { + throw new UtilException(e); + } + } + + /** + * 执行对象中指定方法 + * 如果需要传递的参数为null,请使用NullWrapperBean来传递,不然会丢失类型信息 + * + * @param 返回对象类型 + * @param obj 方法所在对象 + * @param methodName 方法名 + * @param args 参数列表 + * @return 执行结果 + * @throws UtilException IllegalAccessException包装 + * @see NullWrapperBean + * @since 3.1.2 + */ + public static T invoke(final Object obj, final String methodName, final Object... args) throws UtilException { + Assert.notNull(obj, "Object to get method must be not null!"); + Assert.notBlank(methodName, "Method name must be not blank!"); + + final Method method = getMethodOfObj(obj, methodName, args); + if (null == method) { + throw new UtilException("No such method: [{}] from [{}]", methodName, obj.getClass()); + } + return invoke(obj, method, args); + } + + /** + * 执行方法
+ * 可执行Private方法,也可执行static方法
+ * 执行非static方法时,必须满足对象有默认构造方法
+ * 非单例模式,如果是非静态方法,每次创建一个新对象 + * + * @param 对象类型 + * @param classNameWithMethodName 类名和方法名表达式,类名与方法名用{@code .}或{@code #}连接 例如:com.xiaoleilu.hutool.StrUtil.isEmpty 或 com.xiaoleilu.hutool.StrUtil#isEmpty + * @param args 参数,必须严格对应指定方法的参数类型和数量 + * @return 返回结果 + */ + public static T invoke(final String classNameWithMethodName, final Object[] args) { + return invoke(classNameWithMethodName, false, args); + } + + /** + * 执行方法
+ * 可执行Private方法,也可执行static方法
+ * 执行非static方法时,必须满足对象有默认构造方法
+ * + * @param 对象类型 + * @param classNameWithMethodName 类名和方法名表达式,例如:com.xiaoleilu.hutool.StrUtil#isEmpty或com.xiaoleilu.hutool.StrUtil.isEmpty + * @param isSingleton 是否为单例对象,如果此参数为false,每次执行方法时创建一个新对象 + * @param args 参数,必须严格对应指定方法的参数类型和数量 + * @return 返回结果 + */ + public static T invoke(final String classNameWithMethodName, final boolean isSingleton, final Object... args) { + if (StrUtil.isBlank(classNameWithMethodName)) { + throw new UtilException("Blank classNameDotMethodName!"); + } + + int splitIndex = classNameWithMethodName.lastIndexOf('#'); + if (splitIndex <= 0) { + splitIndex = classNameWithMethodName.lastIndexOf('.'); + } + if (splitIndex <= 0) { + throw new UtilException("Invalid classNameWithMethodName [{}]!", classNameWithMethodName); + } + + final String className = classNameWithMethodName.substring(0, splitIndex); + final String methodName = classNameWithMethodName.substring(splitIndex + 1); + + return invoke(className, methodName, isSingleton, args); + } + + /** + * 执行方法
+ * 可执行Private方法,也可执行static方法
+ * 执行非static方法时,必须满足对象有默认构造方法
+ * 非单例模式,如果是非静态方法,每次创建一个新对象 + * + * @param 对象类型 + * @param className 类名,完整类路径 + * @param methodName 方法名 + * @param args 参数,必须严格对应指定方法的参数类型和数量 + * @return 返回结果 + */ + public static T invoke(final String className, final String methodName, final Object[] args) { + return invoke(className, methodName, false, args); + } + + /** + * 执行方法
+ * 可执行Private方法,也可执行static方法
+ * 执行非static方法时,必须满足对象有默认构造方法
+ * + * @param 对象类型 + * @param className 类名,完整类路径 + * @param methodName 方法名 + * @param isSingleton 是否为单例对象,如果此参数为false,每次执行方法时创建一个新对象 + * @param args 参数,必须严格对应指定方法的参数类型和数量 + * @return 返回结果 + */ + public static T invoke(final String className, final String methodName, final boolean isSingleton, final Object... args) { + final Class clazz = ClassLoaderUtil.loadClass(className); + try { + final Method method = MethodUtil.getMethod(clazz, methodName, ClassUtil.getClasses(args)); + if (null == method) { + throw new NoSuchMethodException(StrUtil.format("No such method: [{}]", methodName)); + } + if (ModifierUtil.isStatic(method)) { + return MethodUtil.invoke(null, method, args); + } else { + return MethodUtil.invoke(isSingleton ? Singleton.get(clazz) : clazz.newInstance(), method, args); + } + } catch (final Exception e) { + throw new UtilException(e); + } + } + + /** + * 获取方法的唯一键,结构为: + *
+	 *     返回类型#方法名:参数1类型,参数2类型...
+	 * 
+ * + * @param method 方法 + * @return 方法唯一键 + */ + private static String getUniqueKey(final Method method) { + final StringBuilder sb = new StringBuilder(); + sb.append(method.getReturnType().getName()).append('#'); + sb.append(method.getName()); + final Class[] parameters = method.getParameterTypes(); + for (int i = 0; i < parameters.length; i++) { + if (i == 0) { + sb.append(':'); + } else { + sb.append(','); + } + sb.append(parameters[i].getName()); + } + return sb.toString(); + } + + /** + * 获取类对应接口中的非抽象方法(default方法) + * + * @param clazz 类 + * @return 方法列表 + */ + private static List getDefaultMethodsFromInterface(final Class clazz) { + final List result = new ArrayList<>(); + for (final Class ifc : clazz.getInterfaces()) { + for (final Method m : ifc.getMethods()) { + if (false == ModifierUtil.isAbstract(m)) { + result.add(m); + } + } + } + return result; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java index 0dcd178b5..afeddae2a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java @@ -1,9 +1,6 @@ package cn.hutool.core.reflect; import cn.hutool.core.annotation.Alias; -import cn.hutool.core.bean.NullWrapperBean; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.UniqueKeySet; import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; @@ -17,10 +14,8 @@ import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -43,11 +38,6 @@ public class ReflectUtil { * 字段缓存 */ private static final WeakConcurrentMap, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>(); - /** - * 方法缓存 - */ - private static final WeakConcurrentMap, Method[]> METHODS_CACHE = new WeakConcurrentMap<>(); - // --------------------------------------------------------------------------------------------------------- Constructor /** @@ -374,442 +364,6 @@ public class ReflectUtil { return "this$0".equals(field.getName()); } - // --------------------------------------------------------------------------------------------------------- method - - /** - * 获得指定类本类及其父类中的Public方法名
- * 去重重载的方法 - * - * @param clazz 类 - * @return 方法名Set - */ - public static Set getPublicMethodNames(final Class clazz) { - final HashSet methodSet = new HashSet<>(); - final Method[] methodArray = getPublicMethods(clazz); - if (ArrayUtil.isNotEmpty(methodArray)) { - for (final Method method : methodArray) { - methodSet.add(method.getName()); - } - } - return methodSet; - } - - /** - * 获得本类及其父类所有Public方法 - * - * @param clazz 查找方法的类 - * @return 过滤后的方法列表 - */ - public static Method[] getPublicMethods(final Class clazz) { - return null == clazz ? null : clazz.getMethods(); - } - - /** - * 获得指定类过滤后的Public方法列表
- * - * @param clazz 查找方法的类 - * @param filter 过滤器 - * @return 过滤后的方法数组 - */ - public static Method[] getPublicMethods(final Class clazz, final Filter filter) { - if (null == clazz) { - return null; - } - - final Method[] methods = getPublicMethods(clazz); - if(null == filter){ - return methods; - } - - return ArrayUtil.filter(methods, filter); - } - - /** - * 获得指定类过滤后的Public方法列表 - * - * @param clazz 查找方法的类 - * @param excludeMethods 不包括的方法 - * @return 过滤后的方法列表 - */ - public static Method[] getPublicMethods(final Class clazz, final Method... excludeMethods) { - final HashSet excludeMethodSet = CollUtil.newHashSet(excludeMethods); - return getPublicMethods(clazz, method -> false == excludeMethodSet.contains(method)); - } - - /** - * 获得指定类过滤后的Public方法列表 - * - * @param clazz 查找方法的类 - * @param excludeMethodNames 不包括的方法名列表 - * @return 过滤后的方法数组 - */ - public static Method[] getPublicMethods(final Class clazz, final String... excludeMethodNames) { - final HashSet excludeMethodNameSet = CollUtil.newHashSet(excludeMethodNames); - return getPublicMethods(clazz, method -> false == excludeMethodNameSet.contains(method.getName())); - } - - /** - * 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回{@code null} - * - * @param clazz 类 - * @param methodName 方法名 - * @param paramTypes 参数类型 - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - */ - public static Method getPublicMethod(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { - try { - return clazz.getMethod(methodName, paramTypes); - } catch (final NoSuchMethodException ex) { - return null; - } - } - - /** - * 查找指定对象中的所有方法(包括非public方法),也包括父对象和Object类的方法 - * - *

- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 - *

- * - * @param obj 被查找的对象,如果为{@code null}返回{@code null} - * @param methodName 方法名,如果为空字符串返回{@code null} - * @param args 参数 - * @return 方法 - * @throws SecurityException 无访问权限抛出异常 - */ - public static Method getMethodOfObj(final Object obj, final String methodName, final Object... args) throws SecurityException { - if (null == obj || StrUtil.isBlank(methodName)) { - return null; - } - return getMethod(obj.getClass(), methodName, ClassUtil.getClasses(args)); - } - - /** - * 忽略大小写查找指定方法,如果找不到对应的方法则返回{@code null} - * - *

- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 - *

- * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param methodName 方法名,如果为空字符串返回{@code null} - * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - * @since 3.2.0 - */ - public static Method getMethodIgnoreCase(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { - return getMethod(clazz, true, methodName, paramTypes); - } - - /** - * 查找指定方法 如果找不到对应的方法则返回{@code null} - * - *

- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。 - *

- * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param methodName 方法名,如果为空字符串返回{@code null} - * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - */ - public static Method getMethod(final Class clazz, final String methodName, final Class... paramTypes) throws SecurityException { - return getMethod(clazz, false, methodName, paramTypes); - } - - /** - * 查找指定方法 如果找不到对应的方法则返回{@code null}
- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
- * 如果查找的方法有多个同参数类型重载,查找第一个找到的方法 - * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param ignoreCase 是否忽略大小写 - * @param methodName 方法名,如果为空字符串返回{@code null} - * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - * @since 3.2.0 - */ - public static Method getMethod(final Class clazz, final boolean ignoreCase, final String methodName, final Class... paramTypes) throws SecurityException { - if (null == clazz || StrUtil.isBlank(methodName)) { - return null; - } - - final Method[] methods = getMethods(clazz); - if (ArrayUtil.isNotEmpty(methods)) { - for (final Method method : methods) { - if (StrUtil.equals(methodName, method.getName(), ignoreCase) - && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) - //排除桥接方法,pr#1965@Github - && false == method.isBridge()) { - return method; - } - } - } - return null; - } - - /** - * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} - * - *

- * 此方法只检查方法名是否一致,并不检查参数的一致性。 - *

- * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param methodName 方法名,如果为空字符串返回{@code null} - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - * @since 4.3.2 - */ - public static Method getMethodByName(final Class clazz, final String methodName) throws SecurityException { - return getMethodByName(clazz, false, methodName); - } - - /** - * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} - * - *

- * 此方法只检查方法名是否一致(忽略大小写),并不检查参数的一致性。 - *

- * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param methodName 方法名,如果为空字符串返回{@code null} - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - * @since 4.3.2 - */ - public static Method getMethodByNameIgnoreCase(final Class clazz, final String methodName) throws SecurityException { - return getMethodByName(clazz, true, methodName); - } - - /** - * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null} - * - *

- * 此方法只检查方法名是否一致,并不检查参数的一致性。 - *

- * - * @param clazz 类,如果为{@code null}返回{@code null} - * @param ignoreCase 是否忽略大小写 - * @param methodName 方法名,如果为空字符串返回{@code null} - * @return 方法 - * @throws SecurityException 无权访问抛出异常 - * @since 4.3.2 - */ - public static Method getMethodByName(final Class clazz, final boolean ignoreCase, final String methodName) throws SecurityException { - if (null == clazz || StrUtil.isBlank(methodName)) { - return null; - } - - final Method[] methods = getMethods(clazz); - if (ArrayUtil.isNotEmpty(methods)) { - for (final Method method : methods) { - if (StrUtil.equals(methodName, method.getName(), ignoreCase) - // 排除桥接方法 - && false == method.isBridge()) { - return method; - } - } - } - return null; - } - - /** - * 获得指定类中的Public方法名
- * 去重重载的方法 - * - * @param clazz 类 - * @return 方法名Set - * @throws SecurityException 安全异常 - */ - public static Set getMethodNames(final Class clazz) throws SecurityException { - final HashSet methodSet = new HashSet<>(); - final Method[] methods = getMethods(clazz); - for (final Method method : methods) { - methodSet.add(method.getName()); - } - return methodSet; - } - - /** - * 获得指定类过滤后的Public方法列表 - * - * @param clazz 查找方法的类 - * @param filter 过滤器 - * @return 过滤后的方法列表 - * @throws SecurityException 安全异常 - */ - public static Method[] getMethods(final Class clazz, final Filter filter) throws SecurityException { - if (null == clazz) { - return null; - } - return ArrayUtil.filter(getMethods(clazz), filter); - } - - /** - * 获得一个类中所有方法列表,包括其父类中的方法 - * - * @param beanClass 类,非{@code null} - * @return 方法列表 - * @throws SecurityException 安全检查异常 - */ - public static Method[] getMethods(final Class beanClass) throws SecurityException { - Assert.notNull(beanClass); - return METHODS_CACHE.computeIfAbsent(beanClass, - () -> getMethodsDirectly(beanClass, true, true)); - } - - /** - * 获得一个类中所有方法列表,直接反射获取,无缓存
- * 接口获取方法和默认方法,获取的方法包括: - *
    - *
  • 本类中的所有方法(包括static方法)
  • - *
  • 父类中的所有方法(包括static方法)
  • - *
  • Object中(包括static方法)
  • - *
- * - * @param beanClass 类或接口 - * @param withSupers 是否包括父类或接口的方法列表 - * @param withMethodFromObject 是否包括Object中的方法 - * @return 方法列表 - * @throws SecurityException 安全检查异常 - */ - public static Method[] getMethodsDirectly(final Class beanClass, final boolean withSupers, final boolean withMethodFromObject) throws SecurityException { - Assert.notNull(beanClass); - - if (beanClass.isInterface()) { - // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 - return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods(); - } - - final UniqueKeySet result = new UniqueKeySet<>(true, ReflectUtil::getUniqueKey); - Class searchType = beanClass; - while (searchType != null) { - if (false == withMethodFromObject && Object.class == searchType) { - break; - } - result.addAllIfAbsent(Arrays.asList(searchType.getDeclaredMethods())); - result.addAllIfAbsent(getDefaultMethodsFromInterface(searchType)); - - - searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null; - } - - return result.toArray(new Method[0]); - } - - /** - * 是否为equals方法 - * - * @param method 方法 - * @return 是否为equals方法 - */ - public static boolean isEqualsMethod(final Method method) { - if (method == null || - 1 != method.getParameterCount() || - false == "equals".equals(method.getName())) { - return false; - } - return (method.getParameterTypes()[0] == Object.class); - } - - /** - * 是否为hashCode方法 - * - * @param method 方法 - * @return 是否为hashCode方法 - */ - public static boolean isHashCodeMethod(final Method method) { - return method != null// - && "hashCode".equals(method.getName())// - && isEmptyParam(method); - } - - /** - * 是否为toString方法 - * - * @param method 方法 - * @return 是否为toString方法 - */ - public static boolean isToStringMethod(final Method method) { - return method != null// - && "toString".equals(method.getName())// - && isEmptyParam(method); - } - - /** - * 是否为无参数方法 - * - * @param method 方法 - * @return 是否为无参数方法 - * @since 5.1.1 - */ - public static boolean isEmptyParam(final Method method) { - return method.getParameterCount() == 0; - } - - /** - * 检查给定方法是否为Getter或者Setter方法,规则为:
- *
    - *
  • 方法参数必须为0个或1个
  • - *
  • 如果是无参方法,则判断是否以“get”或“is”开头
  • - *
  • 如果方法参数1个,则判断是否以“set”开头
  • - *
- * - * @param method 方法 - * @return 是否为Getter或者Setter方法 - * @since 5.7.20 - */ - public static boolean isGetterOrSetterIgnoreCase(final Method method) { - return isGetterOrSetter(method, true); - } - - /** - * 检查给定方法是否为Getter或者Setter方法,规则为:
- *
    - *
  • 方法参数必须为0个或1个
  • - *
  • 方法名称不能是getClass
  • - *
  • 如果是无参方法,则判断是否以“get”或“is”开头
  • - *
  • 如果方法参数1个,则判断是否以“set”开头
  • - *
- * - * @param method 方法 - * @param ignoreCase 是否忽略方法名的大小写 - * @return 是否为Getter或者Setter方法 - * @since 5.7.20 - */ - public static boolean isGetterOrSetter(final Method method, final boolean ignoreCase) { - if (null == method) { - return false; - } - - // 参数个数必须为0或1 - final int parameterCount = method.getParameterCount(); - if (parameterCount > 1) { - return false; - } - - String name = method.getName(); - // 跳过getClass这个特殊方法 - if ("getClass".equals(name)) { - return false; - } - if (ignoreCase) { - name = name.toLowerCase(); - } - switch (parameterCount) { - case 0: - return name.startsWith("get") || name.startsWith("is"); - case 1: - return name.startsWith("set"); - default: - return false; - } - } // --------------------------------------------------------------------------------------------------------- newInstance /** @@ -927,142 +481,6 @@ public class ReflectUtil { return null; } - // --------------------------------------------------------------------------------------------------------- invoke - - /** - * 执行静态方法 - * - * @param 对象类型 - * @param method 方法(对象方法或static方法都可) - * @param args 参数对象 - * @return 结果 - * @throws UtilException 多种异常包装 - */ - public static T invokeStatic(final Method method, final Object... args) throws UtilException { - return invoke(null, method, args); - } - - /** - * 执行方法
- * 执行前要检查给定参数: - * - *
-	 * 1. 参数个数是否与方法参数个数一致
-	 * 2. 如果某个参数为null但是方法这个位置的参数为原始类型,则赋予原始类型默认值
-	 * 
- * - * @param 返回对象类型 - * @param obj 对象,如果执行静态方法,此值为{@code null} - * @param method 方法(对象方法或static方法都可) - * @param args 参数对象 - * @return 结果 - * @throws UtilException 一些列异常的包装 - */ - public static T invokeWithCheck(final Object obj, final Method method, final Object... args) throws UtilException { - final Class[] types = method.getParameterTypes(); - if (null != args) { - Assert.isTrue(args.length == types.length, "Params length [{}] is not fit for param length [{}] of method !", args.length, types.length); - Class type; - for (int i = 0; i < args.length; i++) { - type = types[i]; - if (type.isPrimitive() && null == args[i]) { - // 参数是原始类型,而传入参数为null时赋予默认值 - args[i] = ClassUtil.getDefaultValue(type); - } - } - } - - return invoke(obj, method, args); - } - - /** - * 执行方法 - * - *

- * 对于用户传入参数会做必要检查,包括: - * - *

-	 *     1、忽略多余的参数
-	 *     2、参数不够补齐默认值
-	 *     3、传入参数为null,但是目标参数类型为原始类型,做转换
-	 * 
- * - * @param 返回对象类型 - * @param obj 对象,如果执行静态方法,此值为{@code null} - * @param method 方法(对象方法或static方法都可) - * @param args 参数对象 - * @return 结果 - * @throws UtilException 一些列异常的包装 - */ - @SuppressWarnings("unchecked") - public static T invoke(final Object obj, final Method method, final Object... args) throws UtilException { - setAccessible(method); - - // 检查用户传入参数: - // 1、忽略多余的参数 - // 2、参数不够补齐默认值 - // 3、通过NullWrapperBean传递的参数,会直接赋值null - // 4、传入参数为null,但是目标参数类型为原始类型,做转换 - // 5、传入参数类型不对应,尝试转换类型 - final Class[] parameterTypes = method.getParameterTypes(); - final Object[] actualArgs = new Object[parameterTypes.length]; - if (null != args) { - for (int i = 0; i < actualArgs.length; i++) { - if (i >= args.length || null == args[i]) { - // 越界或者空值 - actualArgs[i] = ClassUtil.getDefaultValue(parameterTypes[i]); - } else if (args[i] instanceof NullWrapperBean) { - //如果是通过NullWrapperBean传递的null参数,直接赋值null - actualArgs[i] = null; - } else if (false == parameterTypes[i].isAssignableFrom(args[i].getClass())) { - //对于类型不同的字段,尝试转换,转换失败则使用原对象类型 - final Object targetValue = Convert.convert(parameterTypes[i], args[i]); - if (null != targetValue) { - actualArgs[i] = targetValue; - } - } else { - actualArgs[i] = args[i]; - } - } - } - - if (method.isDefault()) { - // 当方法是default方法时,尤其对象是代理对象,需使用句柄方式执行 - // 代理对象情况下调用method.invoke会导致循环引用执行,最终栈溢出 - return MethodHandleUtil.invokeSpecial(obj, method, args); - } - - try { - return (T) method.invoke(ClassUtil.isStatic(method) ? null : obj, actualArgs); - } catch (final Exception e) { - throw new UtilException(e); - } - } - - /** - * 执行对象中指定方法 - * 如果需要传递的参数为null,请使用NullWrapperBean来传递,不然会丢失类型信息 - * - * @param 返回对象类型 - * @param obj 方法所在对象 - * @param methodName 方法名 - * @param args 参数列表 - * @return 执行结果 - * @throws UtilException IllegalAccessException包装 - * @see NullWrapperBean - * @since 3.1.2 - */ - public static T invoke(final Object obj, final String methodName, final Object... args) throws UtilException { - Assert.notNull(obj, "Object to get method must be not null!"); - Assert.notBlank(methodName, "Method name must be not blank!"); - - final Method method = getMethodOfObj(obj, methodName, args); - if (null == method) { - throw new UtilException("No such method: [{}] from [{}]", methodName, obj.getClass()); - } - return invoke(obj, method, args); - } - /** * 设置方法为可访问(私有方法可以被外部调用) * @@ -1077,47 +495,4 @@ public class ReflectUtil { } return accessibleObject; } - - /** - * 获取方法的唯一键,结构为: - *
-	 *     返回类型#方法名:参数1类型,参数2类型...
-	 * 
- * - * @param method 方法 - * @return 方法唯一键 - */ - private static String getUniqueKey(final Method method) { - final StringBuilder sb = new StringBuilder(); - sb.append(method.getReturnType().getName()).append('#'); - sb.append(method.getName()); - final Class[] parameters = method.getParameterTypes(); - for (int i = 0; i < parameters.length; i++) { - if (i == 0) { - sb.append(':'); - } else { - sb.append(','); - } - sb.append(parameters[i].getName()); - } - return sb.toString(); - } - - /** - * 获取类对应接口中的非抽象方法(default方法) - * - * @param clazz 类 - * @return 方法列表 - */ - private static List getDefaultMethodsFromInterface(final Class clazz) { - final List result = new ArrayList<>(); - for (final Class ifc : clazz.getInterfaces()) { - for (final Method m : ifc.getMethods()) { - if (false == ModifierUtil.isAbstract(m)) { - result.add(m); - } - } - } - return result; - } } diff --git a/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java index 81ef5173b..34827b9e3 100755 --- a/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java @@ -9,9 +9,9 @@ import cn.hutool.core.lang.func.Func1; import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ObjUtil; import java.util.ArrayList; import java.util.Collection; @@ -244,7 +244,7 @@ public class ReUtil { final Map result = MapUtil.newHashMap(m.groupCount()); if (m.find()) { // 通过反射获取 namedGroups 方法 - final Map map = ReflectUtil.invoke(pattern, "namedGroups"); + final Map map = MethodUtil.invoke(pattern, "namedGroups"); map.forEach((key, value) -> result.put(key, m.group(value))); } return result; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java index 607f85d16..5ab0dc774 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjUtil.java @@ -5,10 +5,10 @@ import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.io.SerializeUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.math.NumberUtil; import cn.hutool.core.reflect.ClassUtil; -import cn.hutool.core.reflect.ReflectUtil; -import cn.hutool.core.map.MapUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; import java.lang.reflect.Array; @@ -441,7 +441,7 @@ public class ObjUtil { T result = ArrayUtil.clone(obj); if (null == result) { if (obj instanceof Cloneable) { - result = ReflectUtil.invoke(obj, "clone"); + result = MethodUtil.invoke(obj, "clone"); } else { result = cloneByStream(obj); } diff --git a/hutool-core/src/test/java/cn/hutool/core/reflect/MethodHandleUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/reflect/MethodHandleUtilTest.java index 4f4141e58..ea2426a09 100644 --- a/hutool-core/src/test/java/cn/hutool/core/reflect/MethodHandleUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/reflect/MethodHandleUtilTest.java @@ -1,7 +1,5 @@ package cn.hutool.core.reflect; -import cn.hutool.core.reflect.MethodHandleUtil; -import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.classloader.ClassLoaderUtil; import org.junit.Assert; import org.junit.Test; @@ -23,12 +21,12 @@ public class MethodHandleUtilTest { Assert.assertEquals("Quack", duck.quack()); // 测试子类执行default方法 - final Method quackMethod = ReflectUtil.getMethod(Duck.class, "quack"); + final Method quackMethod = MethodUtil.getMethod(Duck.class, "quack"); String quack = MethodHandleUtil.invokeSpecial(new BigDuck(), quackMethod); Assert.assertEquals("Quack", quack); // 测试反射执行默认方法 - quack = ReflectUtil.invoke(new Duck() {}, quackMethod); + quack = MethodUtil.invoke(new Duck() {}, quackMethod); Assert.assertEquals("Quack", quack); } @@ -37,7 +35,7 @@ public class MethodHandleUtilTest { final Duck duck = (Duck) Proxy.newProxyInstance( ClassLoaderUtil.getClassLoader(), new Class[] { Duck.class }, - ReflectUtil::invoke); + MethodUtil::invoke); Assert.assertEquals("Quack", duck.quack()); } @@ -47,7 +45,7 @@ public class MethodHandleUtilTest { final Duck duck = (Duck) Proxy.newProxyInstance( ClassLoaderUtil.getClassLoader(), new Class[] { Duck.class }, - ReflectUtil::invoke); + MethodUtil::invoke); Assert.assertEquals("Quack", duck.quack()); } @@ -56,7 +54,7 @@ public class MethodHandleUtilTest { public void invokeTest(){ // 测试执行普通方法 final int size = MethodHandleUtil.invokeSpecial(new BigDuck(), - ReflectUtil.getMethod(BigDuck.class, "getSize")); + MethodUtil.getMethod(BigDuck.class, "getSize")); Assert.assertEquals(36, size); } @@ -64,7 +62,7 @@ public class MethodHandleUtilTest { public void invokeStaticTest(){ // 测试执行普通方法 final String result = MethodHandleUtil.invoke(null, - ReflectUtil.getMethod(Duck.class, "getDuck", int.class), 78); + MethodUtil.getMethod(Duck.class, "getDuck", int.class), 78); Assert.assertEquals("Duck 78", result); } diff --git a/hutool-core/src/test/java/cn/hutool/core/reflect/MethodUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/reflect/MethodUtilTest.java new file mode 100644 index 000000000..ad3ab7c7a --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/reflect/MethodUtilTest.java @@ -0,0 +1,169 @@ +package cn.hutool.core.reflect; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.TimeInterval; +import cn.hutool.core.lang.Console; +import cn.hutool.core.lang.test.bean.ExamInfoDict; +import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ArrayUtil; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.lang.reflect.Method; + +public class MethodUtilTest { + @Test + public void getMethodsTest() { + Method[] methods = MethodUtil.getMethods(ExamInfoDict.class); + Assert.assertEquals(20, methods.length); + + //过滤器测试 + methods = MethodUtil.getMethods(ExamInfoDict.class, t -> Integer.class.equals(t.getReturnType())); + + Assert.assertEquals(4, methods.length); + final Method method = methods[0]; + Assert.assertNotNull(method); + + //null过滤器测试 + methods = MethodUtil.getMethods(ExamInfoDict.class, null); + + Assert.assertEquals(20, methods.length); + final Method method2 = methods[0]; + Assert.assertNotNull(method2); + } + + @Test + public void getMethodTest() { + Method method = MethodUtil.getMethod(ExamInfoDict.class, "getId"); + Assert.assertEquals("getId", method.getName()); + Assert.assertEquals(0, method.getParameterTypes().length); + + method = MethodUtil.getMethod(ExamInfoDict.class, "getId", Integer.class); + Assert.assertEquals("getId", method.getName()); + Assert.assertEquals(1, method.getParameterTypes().length); + } + + @Test + public void getMethodIgnoreCaseTest() { + Method method = MethodUtil.getMethodIgnoreCase(ExamInfoDict.class, "getId"); + Assert.assertEquals("getId", method.getName()); + Assert.assertEquals(0, method.getParameterTypes().length); + + method = MethodUtil.getMethodIgnoreCase(ExamInfoDict.class, "GetId"); + Assert.assertEquals("getId", method.getName()); + Assert.assertEquals(0, method.getParameterTypes().length); + + method = MethodUtil.getMethodIgnoreCase(ExamInfoDict.class, "setanswerIs", Integer.class); + Assert.assertEquals("setAnswerIs", method.getName()); + Assert.assertEquals(1, method.getParameterTypes().length); + } + + @Test + public void invokeTest() { + final ReflectUtilTest.AClass testClass = new ReflectUtilTest.AClass(); + MethodUtil.invoke(testClass, "setA", 10); + Assert.assertEquals(10, testClass.getA()); + } + + @Test + @Ignore + public void getMethodBenchTest() { + // 预热 + getMethodWithReturnTypeCheck(ReflectUtilTest.TestBenchClass.class, false, "getH"); + + final TimeInterval timer = DateUtil.timer(); + timer.start(); + for (int i = 0; i < 100000000; i++) { + MethodUtil.getMethod(ReflectUtilTest.TestBenchClass.class, false, "getH"); + } + Console.log(timer.interval()); + + timer.restart(); + for (int i = 0; i < 100000000; i++) { + getMethodWithReturnTypeCheck(ReflectUtilTest.TestBenchClass.class, false, "getH"); + } + Console.log(timer.interval()); + } + + public static Method getMethodWithReturnTypeCheck(final Class clazz, final boolean ignoreCase, final String methodName, final Class... paramTypes) throws SecurityException { + if (null == clazz || StrUtil.isBlank(methodName)) { + return null; + } + + Method res = null; + final Method[] methods = MethodUtil.getMethods(clazz); + if (ArrayUtil.isNotEmpty(methods)) { + for (final Method method : methods) { + if (StrUtil.equals(methodName, method.getName(), ignoreCase) + && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) + && (res == null + || res.getReturnType().isAssignableFrom(method.getReturnType()))) { + res = method; + } + } + } + return res; + } + + @Test + public void getMethodsFromClassExtends() { + // 继承情况下,需解决方法去重问题 + Method[] methods = MethodUtil.getMethods(ReflectUtilTest.C2.class); + Assert.assertEquals(15, methods.length); + + // 排除Object中的方法 + // 3个方法包括类 + methods = MethodUtil.getMethodsDirectly(ReflectUtilTest.C2.class, true, false); + Assert.assertEquals(3, methods.length); + + // getA属于本类 + Assert.assertEquals("public void cn.hutool.core.reflect.ReflectUtilTest$C2.getA()", methods[0].toString()); + // getB属于父类 + Assert.assertEquals("public void cn.hutool.core.reflect.ReflectUtilTest$C1.getB()", methods[1].toString()); + // getC属于接口中的默认方法 + Assert.assertEquals("public default void cn.hutool.core.reflect.ReflectUtilTest$TestInterface1.getC()", methods[2].toString()); + } + + @Test + public void getMethodsFromInterfaceTest() { + // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 + // 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法 + final Method[] methods = MethodUtil.getMethods(ReflectUtilTest.TestInterface3.class); + Assert.assertEquals(4, methods.length); + + // 接口里,调用getMethods和getPublicMethods效果相同 + final Method[] publicMethods = MethodUtil.getPublicMethods(ReflectUtilTest.TestInterface3.class); + Assert.assertArrayEquals(methods, publicMethods); + } + + @Test + public void getPublicMethod() { + final Method superPublicMethod = MethodUtil.getPublicMethod(ReflectUtilTest.TestSubClass.class, "publicMethod"); + Assert.assertNotNull(superPublicMethod); + final Method superPrivateMethod = MethodUtil.getPublicMethod(ReflectUtilTest.TestSubClass.class, "privateMethod"); + Assert.assertNull(superPrivateMethod); + + final Method publicMethod = MethodUtil.getPublicMethod(ReflectUtilTest.TestSubClass.class, "publicSubMethod"); + Assert.assertNotNull(publicMethod); + final Method privateMethod = MethodUtil.getPublicMethod(ReflectUtilTest.TestSubClass.class, "privateSubMethod"); + Assert.assertNull(privateMethod); + } + + @Test + public void getDeclaredMethod() { + final Method noMethod = MethodUtil.getMethod(ReflectUtilTest.TestSubClass.class, "noMethod"); + Assert.assertNull(noMethod); + + final Method privateMethod = MethodUtil.getMethod(ReflectUtilTest.TestSubClass.class, "privateMethod"); + Assert.assertNotNull(privateMethod); + final Method publicMethod = MethodUtil.getMethod(ReflectUtilTest.TestSubClass.class, "publicMethod"); + Assert.assertNotNull(publicMethod); + + final Method publicSubMethod = MethodUtil.getMethod(ReflectUtilTest.TestSubClass.class, "publicSubMethod"); + Assert.assertNotNull(publicSubMethod); + final Method privateSubMethod = MethodUtil.getMethod(ReflectUtilTest.TestSubClass.class, "privateSubMethod"); + Assert.assertNotNull(privateSubMethod); + + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/reflect/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/reflect/ReflectUtilTest.java new file mode 100644 index 000000000..25e9afe72 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/reflect/ReflectUtilTest.java @@ -0,0 +1,178 @@ +package cn.hutool.core.reflect; + +import cn.hutool.core.date.Week; +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; + +/** + * 反射工具类单元测试 + * + * @author Looly + */ +public class ReflectUtilTest { + + @Test + public void getFieldTest() { + // 能够获取到父类字段 + final Field privateField = ReflectUtil.getField(TestSubClass.class, "privateField"); + Assert.assertNotNull(privateField); + } + + @Test + public void getFieldsTest() { + // 能够获取到父类字段 + final Field[] fields = ReflectUtil.getFields(TestSubClass.class); + Assert.assertEquals(4, fields.length); + } + + @Test + public void setFieldTest() { + final AClass testClass = new AClass(); + ReflectUtil.setFieldValue(testClass, "a", "111"); + Assert.assertEquals(111, testClass.getA()); + } + + @Test + public void noneStaticInnerClassTest() { + final NoneStaticClass testAClass = ReflectUtil.newInstanceIfPossible(NoneStaticClass.class); + Assert.assertNotNull(testAClass); + Assert.assertEquals(2, testAClass.getA()); + } + + @Data + static class AClass { + private int a; + } + + @Data + @SuppressWarnings("InnerClassMayBeStatic") + class NoneStaticClass { + private int a = 2; + } + + @Data + static class TestBenchClass { + private int a; + private String b; + private String c; + private String d; + private String e; + private String f; + private String g; + private String h; + private String i; + private String j; + private String k; + private String l; + private String m; + private String n; + } + + interface TestInterface1 { + @SuppressWarnings("unused") + void getA(); + + @SuppressWarnings("unused") + void getB(); + + @SuppressWarnings("unused") + default void getC() { + + } + } + + @SuppressWarnings("AbstractMethodOverridesAbstractMethod") + interface TestInterface2 extends TestInterface1 { + @Override + void getB(); + } + + interface TestInterface3 extends TestInterface2 { + void get3(); + } + + @SuppressWarnings("InnerClassMayBeStatic") + class C1 implements TestInterface2 { + + @Override + public void getA() { + + } + + @Override + public void getB() { + + } + } + + class C2 extends C1 { + @Override + public void getA() { + + } + } + + @Test + public void newInstanceIfPossibleTest(){ + //noinspection ConstantConditions + final int intValue = ReflectUtil.newInstanceIfPossible(int.class); + Assert.assertEquals(0, intValue); + + final Integer integer = ReflectUtil.newInstanceIfPossible(Integer.class); + Assert.assertEquals(new Integer(0), integer); + + final Map map = ReflectUtil.newInstanceIfPossible(Map.class); + Assert.assertNotNull(map); + + final Collection collection = ReflectUtil.newInstanceIfPossible(Collection.class); + Assert.assertNotNull(collection); + + final Week week = ReflectUtil.newInstanceIfPossible(Week.class); + Assert.assertEquals(Week.SUNDAY, week); + + final int[] intArray = ReflectUtil.newInstanceIfPossible(int[].class); + Assert.assertArrayEquals(new int[0], intArray); + } + + @Test + public void getDeclaredField() { + final Field noField = ReflectUtil.getField(TestSubClass.class, "noField"); + Assert.assertNull(noField); + + // 获取不到父类字段 + final Field field = ReflectUtil.getDeClearField(TestSubClass.class, "field"); + Assert.assertNull(field); + + final Field subField = ReflectUtil.getField(TestSubClass.class, "subField"); + Assert.assertNotNull(subField); + } + + @SuppressWarnings("unused") + static class TestClass { + private String privateField; + protected String field; + + private void privateMethod() { + } + + public void publicMethod() { + } + } + + @SuppressWarnings({"InnerClassMayBeStatic", "unused"}) + class TestSubClass extends TestClass { + private String subField; + + private void privateSubMethod() { + } + + public void publicSubMethod() { + } + + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java deleted file mode 100755 index 269f9fea6..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java +++ /dev/null @@ -1,341 +0,0 @@ -package cn.hutool.core.util; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.date.TimeInterval; -import cn.hutool.core.date.Week; -import cn.hutool.core.lang.Console; -import cn.hutool.core.lang.test.bean.ExamInfoDict; -import cn.hutool.core.reflect.ClassUtil; -import cn.hutool.core.reflect.ReflectUtil; -import cn.hutool.core.text.StrUtil; -import lombok.Data; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Map; - -/** - * 反射工具类单元测试 - * - * @author Looly - */ -public class ReflectUtilTest { - - @Test - public void getMethodsTest() { - Method[] methods = ReflectUtil.getMethods(ExamInfoDict.class); - Assert.assertEquals(20, methods.length); - - //过滤器测试 - methods = ReflectUtil.getMethods(ExamInfoDict.class, t -> Integer.class.equals(t.getReturnType())); - - Assert.assertEquals(4, methods.length); - final Method method = methods[0]; - Assert.assertNotNull(method); - - //null过滤器测试 - methods = ReflectUtil.getMethods(ExamInfoDict.class, null); - - Assert.assertEquals(20, methods.length); - final Method method2 = methods[0]; - Assert.assertNotNull(method2); - } - - @Test - public void getMethodTest() { - Method method = ReflectUtil.getMethod(ExamInfoDict.class, "getId"); - Assert.assertEquals("getId", method.getName()); - Assert.assertEquals(0, method.getParameterTypes().length); - - method = ReflectUtil.getMethod(ExamInfoDict.class, "getId", Integer.class); - Assert.assertEquals("getId", method.getName()); - Assert.assertEquals(1, method.getParameterTypes().length); - } - - @Test - public void getMethodIgnoreCaseTest() { - Method method = ReflectUtil.getMethodIgnoreCase(ExamInfoDict.class, "getId"); - Assert.assertEquals("getId", method.getName()); - Assert.assertEquals(0, method.getParameterTypes().length); - - method = ReflectUtil.getMethodIgnoreCase(ExamInfoDict.class, "GetId"); - Assert.assertEquals("getId", method.getName()); - Assert.assertEquals(0, method.getParameterTypes().length); - - method = ReflectUtil.getMethodIgnoreCase(ExamInfoDict.class, "setanswerIs", Integer.class); - Assert.assertEquals("setAnswerIs", method.getName()); - Assert.assertEquals(1, method.getParameterTypes().length); - } - - @Test - public void getFieldTest() { - // 能够获取到父类字段 - final Field privateField = ReflectUtil.getField(TestSubClass.class, "privateField"); - Assert.assertNotNull(privateField); - } - - @Test - public void getFieldsTest() { - // 能够获取到父类字段 - final Field[] fields = ReflectUtil.getFields(TestSubClass.class); - Assert.assertEquals(4, fields.length); - } - - @Test - public void setFieldTest() { - final AClass testClass = new AClass(); - ReflectUtil.setFieldValue(testClass, "a", "111"); - Assert.assertEquals(111, testClass.getA()); - } - - @Test - public void invokeTest() { - final AClass testClass = new AClass(); - ReflectUtil.invoke(testClass, "setA", 10); - Assert.assertEquals(10, testClass.getA()); - } - - @Test - public void noneStaticInnerClassTest() { - final NoneStaticClass testAClass = ReflectUtil.newInstanceIfPossible(NoneStaticClass.class); - Assert.assertNotNull(testAClass); - Assert.assertEquals(2, testAClass.getA()); - } - - @Data - static class AClass { - private int a; - } - - @Data - @SuppressWarnings("InnerClassMayBeStatic") - class NoneStaticClass { - private int a = 2; - } - - @Test - @Ignore - public void getMethodBenchTest() { - // 预热 - getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH"); - - final TimeInterval timer = DateUtil.timer(); - timer.start(); - for (int i = 0; i < 100000000; i++) { - ReflectUtil.getMethod(TestBenchClass.class, false, "getH"); - } - Console.log(timer.interval()); - - timer.restart(); - for (int i = 0; i < 100000000; i++) { - getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH"); - } - Console.log(timer.interval()); - } - - @Data - static class TestBenchClass { - private int a; - private String b; - private String c; - private String d; - private String e; - private String f; - private String g; - private String h; - private String i; - private String j; - private String k; - private String l; - private String m; - private String n; - } - - public static Method getMethodWithReturnTypeCheck(final Class clazz, final boolean ignoreCase, final String methodName, final Class... paramTypes) throws SecurityException { - if (null == clazz || StrUtil.isBlank(methodName)) { - return null; - } - - Method res = null; - final Method[] methods = ReflectUtil.getMethods(clazz); - if (ArrayUtil.isNotEmpty(methods)) { - for (final Method method : methods) { - if (StrUtil.equals(methodName, method.getName(), ignoreCase) - && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) - && (res == null - || res.getReturnType().isAssignableFrom(method.getReturnType()))) { - res = method; - } - } - } - return res; - } - - @Test - public void getMethodsFromClassExtends() { - // 继承情况下,需解决方法去重问题 - Method[] methods = ReflectUtil.getMethods(C2.class); - Assert.assertEquals(15, methods.length); - - // 排除Object中的方法 - // 3个方法包括类 - methods = ReflectUtil.getMethodsDirectly(C2.class, true, false); - Assert.assertEquals(3, methods.length); - - // getA属于本类 - Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C2.getA()", methods[0].toString()); - // getB属于父类 - Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C1.getB()", methods[1].toString()); - // getC属于接口中的默认方法 - Assert.assertEquals("public default void cn.hutool.core.util.ReflectUtilTest$TestInterface1.getC()", methods[2].toString()); - } - - @Test - public void getMethodsFromInterfaceTest() { - // 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法 - // 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法 - final Method[] methods = ReflectUtil.getMethods(TestInterface3.class); - Assert.assertEquals(4, methods.length); - - // 接口里,调用getMethods和getPublicMethods效果相同 - final Method[] publicMethods = ReflectUtil.getPublicMethods(TestInterface3.class); - Assert.assertArrayEquals(methods, publicMethods); - } - - interface TestInterface1 { - @SuppressWarnings("unused") - void getA(); - - @SuppressWarnings("unused") - void getB(); - - @SuppressWarnings("unused") - default void getC() { - - } - } - - @SuppressWarnings("AbstractMethodOverridesAbstractMethod") - interface TestInterface2 extends TestInterface1 { - @Override - void getB(); - } - - interface TestInterface3 extends TestInterface2 { - void get3(); - } - - @SuppressWarnings("InnerClassMayBeStatic") - class C1 implements TestInterface2 { - - @Override - public void getA() { - - } - - @Override - public void getB() { - - } - } - - class C2 extends C1 { - @Override - public void getA() { - - } - } - - @Test - public void newInstanceIfPossibleTest(){ - //noinspection ConstantConditions - final int intValue = ReflectUtil.newInstanceIfPossible(int.class); - Assert.assertEquals(0, intValue); - - final Integer integer = ReflectUtil.newInstanceIfPossible(Integer.class); - Assert.assertEquals(new Integer(0), integer); - - final Map map = ReflectUtil.newInstanceIfPossible(Map.class); - Assert.assertNotNull(map); - - final Collection collection = ReflectUtil.newInstanceIfPossible(Collection.class); - Assert.assertNotNull(collection); - - final Week week = ReflectUtil.newInstanceIfPossible(Week.class); - Assert.assertEquals(Week.SUNDAY, week); - - final int[] intArray = ReflectUtil.newInstanceIfPossible(int[].class); - Assert.assertArrayEquals(new int[0], intArray); - } - - @Test - public void getPublicMethod() { - final Method superPublicMethod = ReflectUtil.getPublicMethod(TestSubClass.class, "publicMethod"); - Assert.assertNotNull(superPublicMethod); - final Method superPrivateMethod = ReflectUtil.getPublicMethod(TestSubClass.class, "privateMethod"); - Assert.assertNull(superPrivateMethod); - - final Method publicMethod = ReflectUtil.getPublicMethod(TestSubClass.class, "publicSubMethod"); - Assert.assertNotNull(publicMethod); - final Method privateMethod = ReflectUtil.getPublicMethod(TestSubClass.class, "privateSubMethod"); - Assert.assertNull(privateMethod); - } - - @Test - public void getDeclaredMethod() { - final Method noMethod = ReflectUtil.getMethod(TestSubClass.class, "noMethod"); - Assert.assertNull(noMethod); - - final Method privateMethod = ReflectUtil.getMethod(TestSubClass.class, "privateMethod"); - Assert.assertNotNull(privateMethod); - final Method publicMethod = ReflectUtil.getMethod(TestSubClass.class, "publicMethod"); - Assert.assertNotNull(publicMethod); - - final Method publicSubMethod = ReflectUtil.getMethod(TestSubClass.class, "publicSubMethod"); - Assert.assertNotNull(publicSubMethod); - final Method privateSubMethod = ReflectUtil.getMethod(TestSubClass.class, "privateSubMethod"); - Assert.assertNotNull(privateSubMethod); - - } - - @Test - public void getDeclaredField() { - final Field noField = ReflectUtil.getField(TestSubClass.class, "noField"); - Assert.assertNull(noField); - - // 获取不到父类字段 - final Field field = ReflectUtil.getDeClearField(TestSubClass.class, "field"); - Assert.assertNull(field); - - final Field subField = ReflectUtil.getField(TestSubClass.class, "subField"); - Assert.assertNotNull(subField); - } - - @SuppressWarnings("unused") - static class TestClass { - private String privateField; - protected String field; - - private void privateMethod() { - } - - public void publicMethod() { - } - } - - @SuppressWarnings({"InnerClassMayBeStatic", "unused"}) - class TestSubClass extends TestClass { - private String subField; - - private void privateSubMethod() { - } - - public void publicSubMethod() { - } - - } -} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java index aa655a883..107ff1cf6 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/TypeUtilTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.util; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.reflect.TypeUtil; import lombok.Data; @@ -15,7 +16,7 @@ public class TypeUtilTest { @Test public void getEleTypeTest() { - final Method method = ReflectUtil.getMethod(TestClass.class, "getList"); + final Method method = MethodUtil.getMethod(TestClass.class, "getList"); final Type type = TypeUtil.getReturnType(method); Assert.assertEquals("java.util.List", type.toString()); @@ -25,7 +26,7 @@ public class TypeUtilTest { @Test public void getParamTypeTest() { - final Method method = ReflectUtil.getMethod(TestClass.class, "intTest", Integer.class); + final Method method = MethodUtil.getMethod(TestClass.class, "intTest", Integer.class); final Type type = TypeUtil.getParamType(method, 0); Assert.assertEquals(Integer.class, type); diff --git a/hutool-cron/src/main/java/cn/hutool/cron/task/InvokeTask.java b/hutool-cron/src/main/java/cn/hutool/cron/task/InvokeTask.java index adbe6ba04..638477ef8 100644 --- a/hutool-cron/src/main/java/cn/hutool/cron/task/InvokeTask.java +++ b/hutool-cron/src/main/java/cn/hutool/cron/task/InvokeTask.java @@ -2,6 +2,7 @@ package cn.hutool.cron.task; import cn.hutool.core.classloader.ClassLoaderUtil; import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.cron.CronException; @@ -50,7 +51,7 @@ public class InvokeTask implements Task{ if(StrUtil.isBlank(methodName)) { throw new IllegalArgumentException("Method name is blank !"); } - this.method = ReflectUtil.getPublicMethod(clazz, methodName); + this.method = MethodUtil.getPublicMethod(clazz, methodName); if(null == this.method) { throw new IllegalArgumentException("No method with name of [" + methodName + "] !"); } @@ -59,7 +60,7 @@ public class InvokeTask implements Task{ @Override public void execute() { try { - ReflectUtil.invoke(this.obj, this.method); + MethodUtil.invoke(this.obj, this.method); } catch (final UtilException e) { throw new CronException(e.getCause()); } diff --git a/hutool-db/src/main/java/cn/hutool/db/Entity.java b/hutool-db/src/main/java/cn/hutool/db/Entity.java index aa2565f05..5f39e70cc 100755 --- a/hutool-db/src/main/java/cn/hutool/db/Entity.java +++ b/hutool-db/src/main/java/cn/hutool/db/Entity.java @@ -3,7 +3,7 @@ package cn.hutool.db; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.map.Dict; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharsetUtil; @@ -301,7 +301,7 @@ public class Entity extends Dict { result = (Time) obj; } catch (final Exception e) { // try oracle.sql.TIMESTAMP - result = ReflectUtil.invoke(obj, "timeValue"); + result = MethodUtil.invoke(obj, "timeValue"); } } return result; @@ -316,7 +316,7 @@ public class Entity extends Dict { result = (Date) obj; } catch (final Exception e) { // try oracle.sql.TIMESTAMP - result = ReflectUtil.invoke(obj, "dateValue"); + result = MethodUtil.invoke(obj, "dateValue"); } } return result; @@ -331,7 +331,7 @@ public class Entity extends Dict { result = (Timestamp) obj; } catch (final Exception e) { // try oracle.sql.TIMESTAMP - result = ReflectUtil.invoke(obj, "timestampValue"); + result = MethodUtil.invoke(obj, "timestampValue"); } } return result; diff --git a/hutool-db/src/main/java/cn/hutool/db/handler/ResultSetUtil.java b/hutool-db/src/main/java/cn/hutool/db/handler/ResultSetUtil.java index 925098e06..50a7ab6b2 100644 --- a/hutool-db/src/main/java/cn/hutool/db/handler/ResultSetUtil.java +++ b/hutool-db/src/main/java/cn/hutool/db/handler/ResultSetUtil.java @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.PropDesc; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.reflect.ReflectUtil; import cn.hutool.core.text.StrUtil; @@ -100,7 +101,7 @@ public class ResultSetUtil { setter = (null == pd) ? null : pd.getSetter(); if (null != setter) { value = getColumnValue(rs, i, meta.getColumnType(i), TypeUtil.getFirstParamType(setter)); - ReflectUtil.invokeWithCheck(bean, setter, value); + MethodUtil.invokeWithCheck(bean, setter, value); } } return bean; diff --git a/hutool-extra/src/main/java/cn/hutool/extra/aop/interceptor/JdkInterceptor.java b/hutool-extra/src/main/java/cn/hutool/extra/aop/interceptor/JdkInterceptor.java index 657ca3491..86f1049ab 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/aop/interceptor/JdkInterceptor.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/aop/interceptor/JdkInterceptor.java @@ -1,8 +1,8 @@ package cn.hutool.extra.aop.interceptor; -import cn.hutool.extra.aop.aspects.Aspect; -import cn.hutool.core.reflect.ClassUtil; +import cn.hutool.core.reflect.ModifierUtil; import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.extra.aop.aspects.Aspect; import java.io.Serializable; import java.lang.reflect.InvocationHandler; @@ -47,7 +47,7 @@ public class JdkInterceptor implements InvocationHandler, Serializable { ReflectUtil.setAccessible(method); try { - result = method.invoke(ClassUtil.isStatic(method) ? null : target, args); + result = method.invoke(ModifierUtil.isStatic(method) ? null : target, args); } catch (final InvocationTargetException e) { // 异常回调(只捕获业务代码导致的异常,而非反射导致的异常) if (aspect.afterException(target, method, args, e.getTargetException())) { diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java index 901285356..ac92be2e7 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java @@ -2,9 +2,9 @@ package cn.hutool.poi.excel.sax; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.reflect.ReflectUtil; +import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.exceptions.POIException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; @@ -20,7 +20,7 @@ import java.util.Iterator; /** * Sax方式读取Excel文件
- * Excel2007格式说明见:http://www.cnblogs.com/wangmingshun/p/6654143.html + * Excel2007格式说明见:http://www.cnblogs.com/wangmingshun/p/6654143.html * * @author Looly * @since 3.1.2 @@ -130,7 +130,7 @@ public class Excel07SaxReader implements ExcelSaxReader { // 获取共享字符串表 // POI-5.2.0开始返回值有所变更,导致实际使用时提示方法未找到,此处使用反射调用,解决不同版本返回值变更问题 //this.handler.sharedStrings = xssfReader.getSharedStringsTable(); - this.handler.sharedStrings = ReflectUtil.invoke(xssfReader, "getSharedStringsTable"); + this.handler.sharedStrings = MethodUtil.invoke(xssfReader, "getSharedStringsTable"); return readSheets(xssfReader, idOrRidOrSheetName); }