diff --git a/CHANGELOG.md b/CHANGELOG.md
index c08f9420e..147ffb5c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
# 5.7.6 (2021-07-28)
### 🐣新特性
+* 【core 】 增加LookupFactory和MethodHandleUtil(issue#I42TVY@Gitee)
+
### 🐞Bug修复
-------------------------------------------------------------------------------------------------------------
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/LookupFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/LookupFactory.java
new file mode 100755
index 000000000..48aa5ff88
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/LookupFactory.java
@@ -0,0 +1,78 @@
+package cn.hutool.core.lang.reflect;
+
+import cn.hutool.core.exceptions.UtilException;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * {@link MethodHandles.Lookup}工厂,用于创建{@link MethodHandles.Lookup}对象
+ * jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}在调用findSpecial和unreflectSpecial
+ * 时会出现权限不够问题,抛出"no private access for invokespecial"异常,因此针对JDK8及JDK9+分别封装lookup方法。
+ *
+ * 参考:
+ *
+ * - https://blog.csdn.net/u013202238/article/details/108687086
+ *
+ *
+ * @author looly
+ * @since 5.7.7
+ */
+public class LookupFactory {
+
+ private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
+ | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
+
+ private static Constructor java8LookupConstructor;
+ private static Method privateLookupInMethod;
+
+ static {
+ //先查询jdk9 开始提供的java.lang.invoke.MethodHandles.privateLookupIn方法,
+ //如果没有说明是jdk8的版本.(不考虑jdk8以下版本)
+ try {
+ //noinspection JavaReflectionMemberAccess
+ privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
+ } catch (NoSuchMethodException ignore) {
+ //ignore
+ }
+
+ //jdk8
+ //这种方式其实也适用于jdk9及以上的版本,但是上面优先,可以避免 jdk9 反射警告
+ if (privateLookupInMethod == null) {
+ try {
+ java8LookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
+ java8LookupConstructor.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ //可能是jdk8 以下版本
+ throw new IllegalStateException(
+ "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);
+ }
+ }
+ }
+
+ /**
+ * jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}在调用findSpecial和unreflectSpecial
+ * 时会出现权限不够问题,抛出"no private access for invokespecial"异常,因此针对JDK8及JDK9+分别封装lookup方法。
+ *
+ * @param callerClass 被调用的类或接口
+ * @return {@link MethodHandles.Lookup}
+ */
+ public static MethodHandles.Lookup lookup(Class> callerClass) {
+ //使用反射,因为当前jdk可能不是java9或以上版本
+ if (privateLookupInMethod != null) {
+ try {
+ return (MethodHandles.Lookup) privateLookupInMethod.invoke(MethodHandles.class, callerClass, MethodHandles.lookup());
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new UtilException(e);
+ }
+ }
+ //jdk 8
+ try {
+ return java8LookupConstructor.newInstance(callerClass, ALLOWED_MODES);
+ } catch (Exception e) {
+ throw new IllegalStateException("no 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java
new file mode 100755
index 000000000..9511a2867
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java
@@ -0,0 +1,65 @@
+package cn.hutool.core.lang.reflect;
+
+import cn.hutool.core.exceptions.UtilException;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+/**
+ * 方法句柄{@link MethodHandle}封装工具类
+ * 参考:
+ *
+ * - https://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-reflectively
+ *
+ *
+ * @author looly
+ * @since 5.7.7
+ */
+public class MethodHandleUtil {
+
+ /**
+ * jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}在调用findSpecial和unreflectSpecial
+ * 时会出现权限不够问题,抛出"no private access for invokespecial"异常,因此针对JDK8及JDK9+分别封装lookup方法。
+ *
+ * @param callerClass 被调用的类或接口
+ * @return {@link MethodHandles.Lookup}
+ */
+ public static MethodHandles.Lookup lookup(Class> callerClass) {
+ return LookupFactory.lookup(callerClass);
+ }
+
+ /**
+ * 执行Interface中的default方法
+ *
+ *
+ * interface Duck {
+ * default String quack() {
+ * return "Quack";
+ * }
+ * }
+ *
+ * Duck duck = (Duck) Proxy.newProxyInstance(
+ * ClassLoaderUtil.getClassLoader(),
+ * new Class[] { Duck.class },
+ * MethodHandleUtil::invokeDefault);
+ *
+ *
+ * @param o 接口的子对象或代理对象
+ * @param method 方法
+ * @param args 参数
+ * @return 结果
+ */
+ @SuppressWarnings("unchecked")
+ public static T invoke(Object o, Method method, Object... args) {
+ final Class> declaringClass = method.getDeclaringClass();
+ try {
+ return (T) lookup(declaringClass)
+ .unreflectSpecial(method, declaringClass)
+ .bindTo(o)
+ .invokeWithArguments(args);
+ } catch (Throwable e) {
+ throw new UtilException(e);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
index 60bc055d2..28b26a661 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
@@ -8,6 +8,7 @@ import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.SimpleCache;
+import cn.hutool.core.lang.reflect.MethodHandleUtil;
import cn.hutool.core.map.MapUtil;
import java.lang.reflect.AccessibleObject;
@@ -916,6 +917,12 @@ public class ReflectUtil {
}
}
+ if(method.isDefault()){
+ // 当方法是default方法时,尤其对象是代理对象,需使用句柄方式执行
+ // 代理对象情况下调用method.invoke会导致循环引用执行,最终栈溢出
+ return MethodHandleUtil.invoke(obj, method, args);
+ }
+
try {
return (T) method.invoke(ClassUtil.isStatic(method) ? null : obj, actualArgs);
} catch (Exception e) {
diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/reflect/MethodHandleUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/reflect/MethodHandleUtilTest.java
new file mode 100755
index 000000000..a955dcd67
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/lang/reflect/MethodHandleUtilTest.java
@@ -0,0 +1,61 @@
+package cn.hutool.core.lang.reflect;
+
+import cn.hutool.core.util.ClassLoaderUtil;
+import cn.hutool.core.util.ReflectUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class MethodHandleUtilTest {
+
+ @Test
+ public void invokeDefaultTest(){
+ Duck duck = (Duck) Proxy.newProxyInstance(
+ ClassLoaderUtil.getClassLoader(),
+ new Class[] { Duck.class },
+ MethodHandleUtil::invoke);
+
+ Assert.assertEquals("Quack", duck.quack());
+
+ // 测试子类执行default方法
+ final Method quackMethod = ReflectUtil.getMethod(Duck.class, "quack");
+ String quack = MethodHandleUtil.invoke(new BigDuck(), quackMethod);
+ Assert.assertEquals("Quack", quack);
+
+ // 测试反射执行默认方法
+ quack = ReflectUtil.invoke(new Duck() {}, quackMethod);
+ Assert.assertEquals("Quack", quack);
+ }
+
+ @Test
+ public void invokeDefaultByReflectTest(){
+ Duck duck = (Duck) Proxy.newProxyInstance(
+ ClassLoaderUtil.getClassLoader(),
+ new Class[] { Duck.class },
+ ReflectUtil::invoke);
+
+ Assert.assertEquals("Quack", duck.quack());
+ }
+
+ @Test
+ public void invokeTest(){
+ // 测试执行普通方法
+ final int size = MethodHandleUtil.invoke(new BigDuck(),
+ ReflectUtil.getMethod(BigDuck.class, "getSize"));
+ Assert.assertEquals(36, size);
+ }
+
+ interface Duck {
+ default String quack() {
+ return "Quack";
+ }
+ }
+
+ static class BigDuck implements Duck{
+ public int getSize(){
+ return 36;
+ }
+ }
+}