add methods

This commit is contained in:
Looly 2021-07-28 23:18:31 +08:00
parent 520b503065
commit 50c9337ea0
3 changed files with 103 additions and 21 deletions

View File

@ -3,6 +3,7 @@ package cn.hutool.core.lang.reflect;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -42,13 +43,16 @@ public class MethodHandleUtil {
* </ul> * </ul>
* *
* @param callerClass 方法所在类或接口 * @param callerClass 方法所在类或接口
* @param name 方法名称 * @param name 方法名称{@link null}或者空则查找构造方法
* @param type 返回类型和参数类型 * @param type 返回类型和参数类型
* @return 方法句柄 {@link MethodHandle}{@code null}表示未找到方法 * @return 方法句柄 {@link MethodHandle}{@code null}表示未找到方法
*/ */
public static MethodHandle findMethod(Class<?> callerClass, String name, MethodType type){ public static MethodHandle findMethod(Class<?> callerClass, String name, MethodType type) {
MethodHandle handle = null; if (StrUtil.isBlank(name)) {
return findConstructor(callerClass, type);
}
MethodHandle handle = null;
final MethodHandles.Lookup lookup = lookup(callerClass); final MethodHandles.Lookup lookup = lookup(callerClass);
try { try {
handle = lookup.findVirtual(callerClass, name, type); handle = lookup.findVirtual(callerClass, name, type);
@ -57,7 +61,7 @@ public class MethodHandleUtil {
} }
// static方法 // static方法
if(null == handle){ if (null == handle) {
try { try {
handle = lookup.findStatic(callerClass, name, type); handle = lookup.findStatic(callerClass, name, type);
} catch (IllegalAccessException | NoSuchMethodException ignore) { } catch (IllegalAccessException | NoSuchMethodException ignore) {
@ -66,7 +70,7 @@ public class MethodHandleUtil {
} }
// 特殊方法包括构造方法私有方法等 // 特殊方法包括构造方法私有方法等
if(null == handle){ if (null == handle) {
try { try {
handle = lookup.findSpecial(callerClass, name, type, callerClass); handle = lookup.findSpecial(callerClass, name, type, callerClass);
} catch (NoSuchMethodException ignore) { } catch (NoSuchMethodException ignore) {
@ -80,7 +84,36 @@ public class MethodHandleUtil {
} }
/** /**
* 执行Interface中的default方法<br> * 查找指定的构造方法
*
* @param callerClass
* @param args 参数
* @return 构造方法句柄
*/
public static MethodHandle findConstructor(Class<?> callerClass, Class<?>... args) {
return findConstructor(callerClass, MethodType.methodType(void.class, args));
}
/**
* 查找指定的构造方法
*
* @param callerClass
* @param type 参数类型此处返回类型应为void.class
* @return 构造方法句柄
*/
public static MethodHandle findConstructor(Class<?> callerClass, MethodType type) {
final MethodHandles.Lookup lookup = lookup(callerClass);
try {
return lookup.findConstructor(callerClass, type);
} catch (NoSuchMethodException e) {
return null;
} catch (IllegalAccessException e) {
throw new UtilException(e);
}
}
/**
* 执行接口或对象中的方法<br>
* *
* <pre class="code"> * <pre class="code">
* interface Duck { * interface Duck {
@ -100,7 +133,7 @@ public class MethodHandleUtil {
* @param args 参数 * @param args 参数
* @return 结果 * @return 结果
*/ */
public static <T> T invoke(Object obj, String methodName, Object... args) { public static <T> T invokeSpecial(Object obj, String methodName, Object... args) {
Assert.notNull(obj, "Object to get method must be not null!"); Assert.notNull(obj, "Object to get method must be not null!");
Assert.notBlank(methodName, "Method name must be not blank!"); Assert.notBlank(methodName, "Method name must be not blank!");
@ -108,11 +141,23 @@ public class MethodHandleUtil {
if (null == method) { if (null == method) {
throw new UtilException("No such method: [{}] from [{}]", methodName, obj.getClass()); throw new UtilException("No such method: [{}] from [{}]", methodName, obj.getClass());
} }
return invoke(obj, method, args); return invokeSpecial(obj, method, args);
} }
/** /**
* 执行Interface中的default方法<br> * 执行接口或对象中的方法
*
* @param obj 接口的子对象或代理对象
* @param method 方法
* @param args 参数
* @return 结果
*/
public static <T> T invoke(Object obj, Method method, Object... args) {
return invoke(false, obj, method, args);
}
/**
* 执行接口或对象中的方法<br>
* *
* <pre class="code"> * <pre class="code">
* interface Duck { * interface Duck {
@ -124,7 +169,32 @@ public class MethodHandleUtil {
* Duck duck = (Duck) Proxy.newProxyInstance( * Duck duck = (Duck) Proxy.newProxyInstance(
* ClassLoaderUtil.getClassLoader(), * ClassLoaderUtil.getClassLoader(),
* new Class[] { Duck.class }, * new Class[] { Duck.class },
* MethodHandleUtil::invokeDefault); * MethodHandleUtil::invoke);
* </pre>
*
* @param obj 接口的子对象或代理对象
* @param method 方法
* @param args 参数
* @return 结果
*/
public static <T> T invokeSpecial(Object obj, Method method, Object... args) {
return invoke(true, obj, method, args);
}
/**
* 执行接口或对象中的方法<br>
*
* <pre class="code">
* interface Duck {
* default String quack() {
* return "Quack";
* }
* }
*
* Duck duck = (Duck) Proxy.newProxyInstance(
* ClassLoaderUtil.getClassLoader(),
* new Class[] { Duck.class },
* MethodHandleUtil::invoke);
* </pre> * </pre>
* *
* @param obj 接口的子对象或代理对象 * @param obj 接口的子对象或代理对象
@ -133,13 +203,17 @@ public class MethodHandleUtil {
* @return 结果 * @return 结果
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T invoke(Object obj, Method method, Object... args) { public static <T> T invoke(boolean isSpecial, Object obj, Method method, Object... args) {
Assert.notNull(method, "Method must be not null!");
final Class<?> declaringClass = method.getDeclaringClass(); final Class<?> declaringClass = method.getDeclaringClass();
final MethodHandles.Lookup lookup = lookup(declaringClass);
try { try {
return (T) lookup(declaringClass) MethodHandle handle = isSpecial ? lookup.unreflectSpecial(method, declaringClass)
.unreflectSpecial(method, declaringClass) : lookup.unreflect(method);
.bindTo(obj) if(null != obj){
.invokeWithArguments(args); handle = handle.bindTo(obj);
}
return (T) handle.invokeWithArguments(args);
} catch (Throwable e) { } catch (Throwable e) {
throw new UtilException(e); throw new UtilException(e);
} }

View File

@ -920,7 +920,7 @@ public class ReflectUtil {
if(method.isDefault()){ if(method.isDefault()){
// 当方法是default方法时尤其对象是代理对象需使用句柄方式执行 // 当方法是default方法时尤其对象是代理对象需使用句柄方式执行
// 代理对象情况下调用method.invoke会导致循环引用执行最终栈溢出 // 代理对象情况下调用method.invoke会导致循环引用执行最终栈溢出
return MethodHandleUtil.invoke(obj, method, args); return MethodHandleUtil.invokeSpecial(obj, method, args);
} }
try { try {

View File

@ -17,13 +17,13 @@ public class MethodHandleUtilTest {
Duck duck = (Duck) Proxy.newProxyInstance( Duck duck = (Duck) Proxy.newProxyInstance(
ClassLoaderUtil.getClassLoader(), ClassLoaderUtil.getClassLoader(),
new Class[] { Duck.class }, new Class[] { Duck.class },
MethodHandleUtil::invoke); MethodHandleUtil::invokeSpecial);
Assert.assertEquals("Quack", duck.quack()); Assert.assertEquals("Quack", duck.quack());
// 测试子类执行default方法 // 测试子类执行default方法
final Method quackMethod = ReflectUtil.getMethod(Duck.class, "quack"); final Method quackMethod = ReflectUtil.getMethod(Duck.class, "quack");
String quack = MethodHandleUtil.invoke(new BigDuck(), quackMethod); String quack = MethodHandleUtil.invokeSpecial(new BigDuck(), quackMethod);
Assert.assertEquals("Quack", quack); Assert.assertEquals("Quack", quack);
// 测试反射执行默认方法 // 测试反射执行默认方法
@ -42,7 +42,7 @@ public class MethodHandleUtilTest {
} }
@Test @Test
public void invokeStaticTest(){ public void invokeStaticByProxyTest(){
Duck duck = (Duck) Proxy.newProxyInstance( Duck duck = (Duck) Proxy.newProxyInstance(
ClassLoaderUtil.getClassLoader(), ClassLoaderUtil.getClassLoader(),
new Class[] { Duck.class }, new Class[] { Duck.class },
@ -54,11 +54,19 @@ public class MethodHandleUtilTest {
@Test @Test
public void invokeTest(){ public void invokeTest(){
// 测试执行普通方法 // 测试执行普通方法
final int size = MethodHandleUtil.invoke(new BigDuck(), final int size = MethodHandleUtil.invokeSpecial(new BigDuck(),
ReflectUtil.getMethod(BigDuck.class, "getSize")); ReflectUtil.getMethod(BigDuck.class, "getSize"));
Assert.assertEquals(36, size); Assert.assertEquals(36, size);
} }
@Test
public void invokeStaticTest(){
// 测试执行普通方法
final String result = MethodHandleUtil.invoke(null,
ReflectUtil.getMethod(Duck.class, "getDuck", int.class), 78);
Assert.assertEquals("Duck 78", result);
}
@Test @Test
public void findMethodTest() throws Throwable { public void findMethodTest() throws Throwable {
MethodHandle handle = MethodHandleUtil.findMethod(Duck.class, "quack", MethodHandle handle = MethodHandleUtil.findMethod(Duck.class, "quack",