有一节课。
class A {
Mono<Response> testMap(Mono<Request> reqMono)
}
有功能界面
interface MapHandler {
Mono<?> handle(Mono<?> reqMono)
}
现在我可以写这个
{
A a = new A();
MapHandler handler = a::testMap;
}
我想构建一个可以检测bean(对象)中的所有MapHandler并收集它们的工具。
我试过这个。
List<MapHandler> list = initedList;
Method method = bean.getClass().getDeclaredMethods()[0];
list.put("methodName", req -> {
return (Mono<?>) method.invoke(bean, req);
})
可以通过MethodHandle
或LambdaMetaFactory
来实现吗?
以您希望的方式明确使用LambdaMetafactory
的解决方案的粗略草图是:
// the signature of the method returned by LambdaMetaFactory
// it takes an object of bean's type and makes a MapHandler that calls one
// of its instance methods
MethodType mkLambdaType = MethodType.methodType(MapHandler.class, bean.getClass());
// the signature of the method in MapHandler being implemented
MethodType handleType = MethodType.methodType(Mono.class, Mono.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
// FYI this won't search in supertypes
// getMethods() will, but you only get public ones even if you have more privileged access
// you decide what to do here; the loop body is the important thing
for(Method method : bean.getClass().getDeclaredMethods()) {
if(Modifier.isStatic(method.getModifiers())) continue;
try {
MethodHandle target = lookup.unreflect(method);
CallSite mkLambda = LambdaMetafactory.metafactory
(lookup, "handle", mkLambdaType, handleType, target, handleType);
list.add((MapHandler)mkLambda.getTarget().invoke(bean));
} catch(IllegalAccessException | LambdaConversionException e) {
// because I am lazy, I'm not checking that method has the correct type
// I'm letting LambdaMetafactory throw if that's not the case
// if you choose not to use LambdaMetafactory, you may have to implement
// this type-checking
continue;
} catch(Throwable t) {
// Throwables come from the MethodHandle#invoke call
// but nothing should be thrown at all, because LambdaMetafactory
// produces its errors from metafactory, early, which are caught above
throw new RuntimeException("Unexpected error during reflection", t);
}
}
我相信这很浪费。 LambdaMetafactory
的常见实现可能会在metafactory
调用中创建一个全新的类,返回指向构造函数或类似的CallSite
。这意味着你得到的每个MapHandler
都是它自己的匿名类,在运行时创建。相比之下,使用lambda调用Method
的最初想法对于JVM来说要好得多。 lambda导致创建单个LambdaMetafactory
类,它将method
和bean
保存为实例变量。在第一次运行代码之后,invokedynamic
被引导,每个MapHandler
只是通过实例化这个匿名类来创建。如果你只需要相对较少的LambdaMetafactory
s,我的MapHandler
解决方案似乎是可以接受的,但是每个都经常被调用,以至于Method#invoke
的开销太高了。
所以我已经完成了一些快速的基准测试。在你的用例中,你在程序启动时初始化MapHandler
s,然后只是调用它们,我的技术和你的技术大致相同。它们都是如此之快,我实际上无法衡量调用不同种类的MapHandler
所花费的时间差。一般来说,LambdaMetafactory
更糟糕,因为创建匿名类需要花费很多时间。事实上,你制作的课程越多,花费的时间就越长。同时,method.invoke
lambda只需构造一个对象,通常要快几千倍。