!960 [enhancement] 使LambdaFactory.build支持构造器

Merge pull request !960 from 阿超/v6-dev
This commit is contained in:
Looly 2023-03-15 07:56:29 +00:00 committed by Gitee
commit d9cd597256
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
2 changed files with 36 additions and 19 deletions

View File

@ -7,9 +7,12 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.LookupFactory;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.reflect.ReflectUtil;
import java.io.Serializable;
import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@ -31,7 +34,7 @@ public class LambdaFactory {
throw new IllegalAccessException();
}
private static final Map<MutableEntry<Class<?>, Method>, Object> CACHE = new WeakConcurrentMap<>();
private static final Map<MutableEntry<Class<?>, Executable>, Object> CACHE = new WeakConcurrentMap<>();
/**
* 构建Lambda
@ -61,30 +64,37 @@ public class LambdaFactory {
* 构建Lambda
*
* @param functionInterfaceType 接受Lambda的函数式接口类型
* @param method 方法对象
* @param executable 方法对象支持构造器
* @param <F> Function类型
* @return 接受Lambda的函数式接口对象
* @param <F> Function类型
*/
@SuppressWarnings("unchecked")
public static <F> F build(final Class<F> functionInterfaceType, final Method method) {
public static <F> F build(final Class<F> functionInterfaceType, final Executable executable) {
Assert.notNull(functionInterfaceType);
Assert.notNull(method);
final MutableEntry<Class<?>, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method);
Assert.notNull(executable);
final MutableEntry<Class<?>, Executable> cacheKey = new MutableEntry<>(functionInterfaceType, executable);
return (F) CACHE.computeIfAbsent(cacheKey, key -> {
final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
.filter(m -> Modifier.isAbstract(m.getModifiers()))
.collect(Collectors.toList());
Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口");
if (!method.isAccessible()) {
method.setAccessible(true);
}
ReflectUtil.setAccessible(executable);
final Method invokeMethod = abstractMethods.get(0);
final MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass());
final MethodHandles.Lookup caller = LookupFactory.lookup(executable.getDeclaringClass());
final String invokeName = invokeMethod.getName();
final MethodType invokedType = methodType(functionInterfaceType);
final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes());
final MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get();
final MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
final MethodHandle implMethod;
final MethodType instantiatedMethodType;
if (executable instanceof Method) {
final Method method = (Method) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflect(method)).get();
instantiatedMethodType = MethodType.methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
} else {
final Constructor<?> constructor = (Constructor<?>) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflectConstructor(constructor)).get();
instantiatedMethodType = MethodType.methodType(constructor.getDeclaringClass(), constructor.getParameterTypes());
}
final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType);
try {
final CallSite callSite = isSerializable ?
@ -94,7 +104,7 @@ public class LambdaFactory {
invokedType,
samMethodType,
implMethod,
insMethodType,
instantiatedMethodType,
FLAG_SERIALIZABLE
) :
LambdaMetafactory.metafactory(
@ -103,7 +113,7 @@ public class LambdaFactory {
invokedType,
samMethodType,
implMethod,
insMethodType
instantiatedMethodType
);
//noinspection unchecked
return (F) callSite.getTarget().invoke();

View File

@ -12,11 +12,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.lang.invoke.LambdaConversionException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
@ -24,6 +21,7 @@ import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author nasodaengineer
@ -160,6 +158,15 @@ public class LambdaFactoryTest {
loop(count, tasks);
}
@Test
public void testConstructor() {
Constructor<Something> constructor = ((SerSupplier<Constructor<Something>>) Something.class::getConstructor).get();
Supplier<Something> constructorLambda = LambdaFactory.build(Supplier.class, constructor);
// constructorLambda can be cache or transfer
Something something = constructorLambda.get();
Assert.assertEquals(Something.class, something.getClass());
}
/**
* <p>hardCode 运行1次耗时 7600 NANOSECONDS
* <p>lambda 运行1次耗时 12400 NANOSECONDS