重构LambdaUtil,强化原有的SerializedLambda缓存为自定义LambdaInfo,该类额外存储了lambda反射类,且可以获取到lambda中使用的外部变量参数;额外在ReflectUtil新增了部分函数,用于获取描述符

This commit is contained in:
achao 2022-06-04 00:22:27 +08:00 committed by VampireAchao
parent 41bd03fa25
commit c4a2be8120
4 changed files with 313 additions and 122 deletions

View File

@ -0,0 +1,78 @@
package cn.hutool.core.lang.func;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.text.StrUtil;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* 存放lambda信息
*
* @author VampireAchao
*/
public class LambdaInfo {
private Type instantiatedType;
private final Type[] parameterTypes;
private final Type returnType;
private final String name;
private final Executable executable;
private final Class<?> clazz;
private final SerializedLambda lambda;
public LambdaInfo(Executable executable, SerializedLambda lambda) {
if (executable instanceof Method) {
Method method = (Method) executable;
this.parameterTypes = method.getGenericParameterTypes();
this.returnType = method.getGenericReturnType();
this.name = method.getName();
} else if (executable instanceof Constructor) {
Constructor<?> constructor = (Constructor<?>) executable;
this.parameterTypes = constructor.getGenericParameterTypes();
this.returnType = constructor.getDeclaringClass();
this.name = constructor.getName();
} else {
throw new IllegalArgumentException("Unsupported executable type: " + executable.getClass());
}
int index = lambda.getInstantiatedMethodType().indexOf(";)");
if (index > -1) {
this.instantiatedType = ClassLoaderUtil.loadClass(StrUtil.sub(lambda.getInstantiatedMethodType(), 2, index));
}
this.clazz = (Class<?>) FieldUtil.getFieldValue(executable, "clazz");
this.executable = executable;
this.lambda = lambda;
}
public Type getInstantiatedType() {
return instantiatedType;
}
public Type[] getParameterTypes() {
return parameterTypes;
}
public Type getReturnType() {
return returnType;
}
public String getName() {
return name;
}
public Executable getExecutable() {
return executable;
}
public Class<?> getClazz() {
return clazz;
}
public SerializedLambda getLambda() {
return lambda;
}
}

View File

@ -2,13 +2,18 @@ package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.classloader.ClassLoaderUtil; import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.StrUtil; import cn.hutool.core.reflect.ReflectUtil;
import java.io.Serializable; import java.io.Serializable;
import java.lang.invoke.MethodHandleInfo;
import java.lang.invoke.SerializedLambda; import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;
/** /**
* Lambda相关工具类 * Lambda相关工具类
@ -18,7 +23,7 @@ import java.lang.invoke.SerializedLambda;
*/ */
public class LambdaUtil { public class LambdaUtil {
private static final WeakConcurrentMap<String, SerializedLambda> cache = new WeakConcurrentMap<>(); private static final WeakConcurrentMap<String, LambdaInfo> CACHE = new WeakConcurrentMap<>();
/** /**
* 通过对象的方法或类的静态方法引用获取lambda实现类 * 通过对象的方法或类的静态方法引用获取lambda实现类
@ -50,39 +55,49 @@ public class LambdaUtil {
* @param func lambda * @param func lambda
* @param <R> 类型 * @param <R> 类型
* @return lambda实现类 * @return lambda实现类
* @throws IllegalArgumentException 如果是不支持的方法引用抛出该异常{@link LambdaUtil#checkLambdaTypeCanGetClass}
* @since 5.8.0
* @author VampireAchao * @author VampireAchao
*/ */
public static <R> Class<R> getRealClass(final Func0<?> func) { @SuppressWarnings("unchecked")
final SerializedLambda lambda = resolve(func); public static <R> Class<R> getRealClass(final Serializable func) {
checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); LambdaInfo lambdaInfo = resolve(func);
return ClassLoaderUtil.loadClass(lambda.getImplClass()); return (Class<R>) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedType).orElseGet(lambdaInfo::getClazz);
} }
/** /**
* 解析lambda表达式,加了缓存 * 解析lambda表达式,加了缓存
* 该缓存可能会在任意不定的时间被清除 * 该缓存可能会在任意不定的时间被清除
* *
* @param <T> Lambda类型
* @param func 需要解析的 lambda 对象无参方法 * @param func 需要解析的 lambda 对象无参方法
* @return 返回解析后的结果 * @return 返回解析后的结果
*/ */
public static <T> SerializedLambda resolve(final Func1<T, ?> func) { public static LambdaInfo resolve(final Serializable func) {
return _resolve(func); return CACHE.computeIfAbsent(func.getClass().getName(), (key) -> {
} final SerializedLambda serializedLambda = _resolve(func);
final String methodName = serializedLambda.getImplMethodName();
/** final Class<?> implClass;
* 解析lambda表达式,加了缓存 ClassLoaderUtil.loadClass(serializedLambda.getImplClass().replace("/", "."), true);
* 该缓存可能会在任意不定的时间被清除 try {
* implClass = Class.forName(serializedLambda.getImplClass().replace("/", "."), true, Thread.currentThread().getContextClassLoader());
* @param <R> Lambda返回类型 } catch (ClassNotFoundException e) {
* @param func 需要解析的 lambda 对象无参方法 throw new UtilException(e);
* @return 返回解析后的结果 }
* @since 5.7.23 if ("<init>".equals(methodName)) {
*/ for (Constructor<?> constructor : implClass.getDeclaredConstructors()) {
public static <R> SerializedLambda resolve(final Func0<R> func) { if (ReflectUtil.getDescriptor(constructor).equals(serializedLambda.getImplMethodSignature())) {
return _resolve(func); return new LambdaInfo(constructor, serializedLambda);
}
}
} else {
Method[] methods = MethodUtil.getMethods(implClass);
for (Method method : methods) {
if (method.getName().equals(methodName)
&& ReflectUtil.getDescriptor(method).equals(serializedLambda.getImplMethodSignature())) {
return new LambdaInfo(method, serializedLambda);
}
}
}
throw new IllegalStateException("No lambda method found.");
});
} }
/** /**
@ -93,7 +108,7 @@ public class LambdaUtil {
* @return 函数名称 * @return 函数名称
*/ */
public static <P> String getMethodName(final Func1<P, ?> func) { public static <P> String getMethodName(final Func1<P, ?> func) {
return resolve(func).getImplMethodName(); return resolve(func).getName();
} }
/** /**
@ -105,36 +120,7 @@ public class LambdaUtil {
* @since 5.7.23 * @since 5.7.23
*/ */
public static <R> String getMethodName(final Func0<R> func) { public static <R> String getMethodName(final Func0<R> func) {
return resolve(func).getImplMethodName(); return resolve(func).getName();
}
/**
* 通过对象的方法或类的静态方法引用然后根据{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类<br>
* 传入lambda有参数且含有返回值的情况能够匹配到此方法
* <ul>
* <li>引用特定类型的任意对象的实例方法<pre>{@code
* Class<MyTeacher> functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, functionClass);
* }</pre></li>
* <li>引用静态带参方法<pre>{@code
* Class<MyTeacher> staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy);
* Assert.assertEquals(MyTeacher.class, staticFunctionClass);
* }</pre></li>
* </ul>
*
* @param func lambda
* @param <P> 方法调用方类型
* @param <R> 返回值类型
* @return lambda实现类
* @throws IllegalArgumentException 如果是不支持的方法引用抛出该异常{@link LambdaUtil#checkLambdaTypeCanGetClass}
* @since 5.8.0
* @author VampireAchao
*/
public static <P, R> Class<P> getRealClass(final Func1<P, R> func) {
final SerializedLambda lambda = resolve(func);
checkLambdaTypeCanGetClass(lambda.getImplMethodKind());
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
return ClassLoaderUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
} }
/** /**
@ -176,18 +162,6 @@ public class LambdaUtil {
} }
//region Private methods //region Private methods
/**
* 检查是否为支持的类型
*
* @param implMethodKind 支持的lambda类型
* @throws IllegalArgumentException 如果是不支持的方法引用抛出该异常
*/
private static void checkLambdaTypeCanGetClass(final int implMethodKind) {
if (implMethodKind != MethodHandleInfo.REF_invokeVirtual &&
implMethodKind != MethodHandleInfo.REF_invokeStatic) {
throw new IllegalArgumentException("该lambda不是合适的方法引用");
}
}
/** /**
* 解析lambda表达式,加了缓存 * 解析lambda表达式,加了缓存
@ -202,7 +176,21 @@ public class LambdaUtil {
* @return 返回解析后的结果 * @return 返回解析后的结果
*/ */
private static SerializedLambda _resolve(final Serializable func) { private static SerializedLambda _resolve(final Serializable func) {
return cache.computeIfAbsent(func.getClass().getName(), (key) -> MethodUtil.invoke(func, "writeReplace")); if (func instanceof SerializedLambda) {
return (SerializedLambda) func;
}
if (func instanceof Proxy) {
throw new UtilException("not support proxy, just for now");
}
final Class<? extends Serializable> clazz = func.getClass();
if (!clazz.isSynthetic()) {
throw new UtilException("Not a lambda expression: " + clazz.getName());
}
final Object serLambda = MethodUtil.invoke(func, "writeReplace");
if (Objects.nonNull(serLambda) && serLambda instanceof SerializedLambda) {
return (SerializedLambda) serLambda;
}
throw new UtilException("writeReplace result value is not java.lang.invoke.SerializedLambda");
} }
//endregion //endregion
} }

View File

@ -1,6 +1,9 @@
package cn.hutool.core.reflect; package cn.hutool.core.reflect;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
/** /**
* 反射工具类 * 反射工具类
@ -24,4 +27,105 @@ public class ReflectUtil {
} }
return accessibleObject; return accessibleObject;
} }
/**
* 获取描述符
*
* @param executable 可执行的反射对象
* @return 描述符
*/
public static String getDescriptor(Executable executable) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append('(');
Class<?>[] parameters = executable.getParameterTypes();
for (Class<?> parameter : parameters) {
appendDescriptor(parameter, stringBuilder);
}
if (executable instanceof Method) {
Method method = (Method) executable;
stringBuilder.append(')');
appendDescriptor(method.getReturnType(), stringBuilder);
return stringBuilder.toString();
} else if (executable instanceof Constructor) {
return stringBuilder.append(")V").toString();
}
throw new IllegalArgumentException("Unknown Executable: " + executable);
}
private static void appendDescriptor(Class<?> clazz, StringBuilder stringBuilder) {
Class<?> currentClass;
for (currentClass = clazz;
currentClass.isArray();
currentClass = currentClass.getComponentType()) {
stringBuilder.append('[');
}
if (currentClass.isPrimitive()) {
final char descriptor;
// see sun.invoke.util.Wrapper
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
if (currentClass == boolean.class) {
descriptor = 'Z';
} else if (currentClass == byte.class) {
descriptor = 'B';
} else if (currentClass == short.class) {
descriptor = 'S';
} else if (currentClass == char.class) {
descriptor = 'C';
} else if (currentClass == int.class) {
descriptor = 'I';
} else if (currentClass == long.class) {
descriptor = 'J';
} else if (currentClass == float.class) {
descriptor = 'F';
} else if (currentClass == double.class) {
descriptor = 'D';
} else if (currentClass == Object.class) {
descriptor = 'L';
} else if (currentClass == void.class) {
// VOID must be the last type, since it is "assignable" from any other type:
descriptor = 'V';
} else {
throw new AssertionError();
}
stringBuilder.append(descriptor);
} else {
stringBuilder.append('L').append(currentClass.getName().replace('.', '/')).append(';');
}
}
private static char getDescriptorChar(Class<?> currentClass) {
if (currentClass == Boolean.class || currentClass == boolean.class) {
return 'Z';
}
if (currentClass == Byte.class || currentClass == byte.class) {
return 'B';
}
if (currentClass == Short.class || currentClass == short.class) {
return 'S';
}
if (currentClass == Character.class || currentClass == char.class) {
return 'C';
}
if (currentClass == Integer.class || currentClass == int.class) {
return 'I';
}
if (currentClass == Long.class || currentClass == long.class) {
return 'J';
}
if (currentClass == Float.class || currentClass == float.class) {
return 'F';
}
if (currentClass == Double.class || currentClass == double.class) {
return 'D';
}
if (currentClass == Object.class) {
return 'L';
}
if (currentClass == Void.class || currentClass == void.class) {
return 'V';
}
throw new AssertionError();
}
} }

View File

@ -1,14 +1,15 @@
package cn.hutool.core.lang.func; package cn.hutool.core.lang.func;
import org.junit.Assert;
import org.junit.Test;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.junit.Assert;
import org.junit.Test;
import java.lang.invoke.MethodHandleInfo; import java.lang.reflect.Array;
import java.util.stream.Stream;
@SuppressWarnings("unchecked")
public class LambdaUtilTest { public class LambdaUtilTest {
@Test @Test
@ -25,60 +26,80 @@ public class LambdaUtilTest {
@Test @Test
public void resolveTest() { public void resolveTest() {
// 引用构造函数 Stream.<Runnable>of(() -> {
Assert.assertEquals(MethodHandleInfo.REF_newInvokeSpecial, // 引用构造函数
LambdaUtil.resolve(MyTeacher::new).getImplMethodKind()); Func0<MyTeacher> lambda = MyTeacher::new;
// 数组构造函数引用 LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
Assert.assertEquals(MethodHandleInfo.REF_invokeStatic, Assert.assertEquals(0, lambdaInfo.getParameterTypes().length);
LambdaUtil.resolve(MyTeacher[]::new).getImplMethodKind()); Assert.assertEquals(MyTeacher.class, lambdaInfo.getReturnType());
// 引用静态方法 }, () -> {
Assert.assertEquals(MethodHandleInfo.REF_invokeStatic, // 数组构造函数引用(此处数组构造参数)
LambdaUtil.resolve(MyTeacher::takeAge).getImplMethodKind()); Func1<Integer, MyTeacher[]> lambda = MyTeacher[]::new;
// 引用特定对象的实例方法 LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual, Assert.assertEquals(int.class, lambdaInfo.getParameterTypes()[0]);
LambdaUtil.resolve(new MyTeacher()::getAge).getImplMethodKind()); Assert.assertEquals(MyTeacher.class, ((Class<Array>) lambdaInfo.getReturnType()).getComponentType());
// 引用特定类型的任意对象的实例方法 }, () -> {
Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual, // 引用静态方法
LambdaUtil.resolve(MyTeacher::getAge).getImplMethodKind()); Func0<String> noArgsStaticMethod = MyTeacher::takeAge;
LambdaInfo lambdaInfo = LambdaUtil.resolve(noArgsStaticMethod);
Assert.assertEquals(String.class, lambdaInfo.getReturnType());
}, () -> {
// 引用特定对象的实例方法
Func0<String> instantiated = new MyTeacher()::getAge;
LambdaInfo lambdaInfo = LambdaUtil.resolve(instantiated);
Assert.assertEquals(String.class, lambdaInfo.getReturnType());
}, () -> {
// 引用特定类型的任意对象的实例方法
Func1<MyTeacher, String> annoInstantiated = MyTeacher::getAge;
LambdaInfo lambdaInfo = LambdaUtil.resolve(annoInstantiated);
Assert.assertEquals(String.class, lambdaInfo.getReturnType());
}).forEach(Runnable::run);
} }
@Test @Test
public void getRealClassTest() { public void getRealClassTest() {
// 引用特定类型的任意对象的实例方法
final Class<MyTeacher> functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
Assert.assertEquals(MyTeacher.class, functionClass);
// 枚举测试不会导致类型擦除
final Class<LambdaKindEnum> enumFunctionClass = LambdaUtil.getRealClass(LambdaKindEnum::ordinal);
Assert.assertEquals(LambdaKindEnum.class, enumFunctionClass);
// 调用父类方法能获取到正确的子类类型
final Class<MyTeacher> superFunctionClass = LambdaUtil.getRealClass(MyTeacher::getId);
Assert.assertEquals(MyTeacher.class, superFunctionClass);
final MyTeacher myTeacher = new MyTeacher(); final MyTeacher myTeacher = new MyTeacher();
// 引用特定对象的实例方法 Stream.<Runnable>of(() -> {
final Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge); // 引用特定类型的任意对象的实例方法
Assert.assertEquals(MyTeacher.class, supplierClass); final Func1<MyTeacher, String> lambda = MyTeacher::getAge;
// 枚举测试只能获取到枚举类型 Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
final Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaKindEnum.REF_NONE::ordinal); }, () -> {
Assert.assertEquals(Enum.class, enumSupplierClass); // 枚举测试不会导致类型擦除
// 调用父类方法只能获取到父类类型 final Func1<LambdaKindEnum, Integer> lambda = LambdaKindEnum::ordinal;
final Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId); Assert.assertEquals(LambdaKindEnum.class, LambdaUtil.getRealClass(lambda));
Assert.assertEquals(Entity.class, superSupplierClass); }, () -> {
// 调用父类方法能获取到正确的子类类型
// 引用静态带参方法能够获取到正确的参数类型 final Func1<MyTeacher, ?> lambda = MyTeacher::getId;
final Class<MyTeacher> staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy); Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
Assert.assertEquals(MyTeacher.class, staticFunctionClass); }, () -> {
// 引用父类静态带参方法只能获取到父类类型 // 引用特定对象的实例方法
final Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId); Func0<String> lambda = myTeacher::getAge;
Assert.assertEquals(Entity.class, staticSuperFunctionClass); Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
}, () -> {
// 引用静态无参方法能够获取到正确的类型 // 枚举测试只能获取到枚举类型
final Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge); Func0<Integer> lambda = LambdaKindEnum.REF_NONE::ordinal;
Assert.assertEquals(MyTeacher.class, staticSupplierClass); Assert.assertEquals(Enum.class, LambdaUtil.getRealClass(lambda));
// 引用父类静态无参方法能够获取到正确的参数类型 }, () -> {
final Class<MyTeacher> staticSuperSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeIdBy); // 调用父类方法只能获取到父类类型
Assert.assertEquals(MyTeacher.class, staticSuperSupplierClass); VoidFunc0 lambda = myTeacher::getId;
Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda));
}, () -> {
// 引用静态带参方法能够获取到正确的参数类型
Func1<MyTeacher, String> lambda = MyTeacher::takeAgeBy;
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
}, () -> {
// 引用父类静态带参方法只能获取到父类类型
Func0<?> lambda = MyTeacher::takeId;
Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda));
}, () -> {
// 引用静态无参方法能够获取到正确的类型
Func0<String> lambda = MyTeacher::takeAge;
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
}, () -> {
// 引用父类静态无参方法能够获取到正确的参数类型
Func1<MyTeacher, ?> lambda = MyTeacher::takeIdBy;
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
}).forEach(Runnable::run);
} }
@Data @Data