mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-05 13:17:58 +08:00
add LookupFactory and MethodHandleUtil
This commit is contained in:
parent
62df0171a4
commit
05495ba8ed
@ -6,6 +6,8 @@
|
||||
# 5.7.6 (2021-07-28)
|
||||
|
||||
### 🐣新特性
|
||||
* 【core 】 增加LookupFactory和MethodHandleUtil(issue#I42TVY@Gitee)
|
||||
|
||||
### 🐞Bug修复
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
78
hutool-core/src/main/java/cn/hutool/core/lang/reflect/LookupFactory.java
Executable file
78
hutool-core/src/main/java/cn/hutool/core/lang/reflect/LookupFactory.java
Executable file
@ -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}对象<br>
|
||||
* jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}在调用findSpecial和unreflectSpecial
|
||||
* 时会出现权限不够问题,抛出"no private access for invokespecial"异常,因此针对JDK8及JDK9+分别封装lookup方法。
|
||||
*
|
||||
* 参考:
|
||||
* <ul>
|
||||
* <li>https://blog.csdn.net/u013202238/article/details/108687086</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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<MethodHandles.Lookup> 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);
|
||||
}
|
||||
}
|
||||
}
|
65
hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java
Executable file
65
hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java
Executable file
@ -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}封装工具类<br>
|
||||
* 参考:
|
||||
* <ul>
|
||||
* <li>https://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-reflectively</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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方法<br>
|
||||
*
|
||||
* <pre class="code">
|
||||
* interface Duck {
|
||||
* default String quack() {
|
||||
* return "Quack";
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Duck duck = (Duck) Proxy.newProxyInstance(
|
||||
* ClassLoaderUtil.getClassLoader(),
|
||||
* new Class[] { Duck.class },
|
||||
* MethodHandleUtil::invokeDefault);
|
||||
* </pre>
|
||||
*
|
||||
* @param o 接口的子对象或代理对象
|
||||
* @param method 方法
|
||||
* @param args 参数
|
||||
* @return 结果
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user