LambdaMetafactory 在不同的 ClassLoader 上访问 class

LambdaMetafactory to access class on a different ClassLoader

我有这段代码可以正常工作:

Method getterMethod = Person.class.getDeclaredMethod("getName");

MethodHandles.Lookup lookup = MethodHandles.publicLookup();
Class<?> declaringClass = getterMethod.getDeclaringClass();
Class<?> returnType = getterMethod.getReturnType();
CallSite getterSite = LambdaMetafactory.metafactory(lookup,
    "apply",
    MethodType.methodType(Function.class),
    MethodType.methodType(Object.class, Object.class),
    lookup.findVirtual(declaringClass, getterMethod.getName(), MethodType.methodType(returnType)),
    MethodType.methodType(propertyType, declaringClass));

Function getterFunction = (Function) getterSite.getTarget().invokeExact();

但是如果 getterMethod 是从不同的 ClassLoader 加载的 class 的方法,它会抛出:

Caused by: java.lang.invoke.LambdaConversionException: Invalid caller: java.lang.Object
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:118)
    at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)

如何将我的 ClassLoader 实例传递给 LambdaMetafactory

LambdaMetafactory 的方法要求您将 PRIVATE 查找作为其第一个参数传递。规范 caller - Represents a lookup context with the accessibility privileges of the caller. 可能有点晦涩,但它应该给出关于 Lookup.

所需特权的想法

为什么它必须是私有的? 这是对 API 的未来验证。请记住,LambdaMetafactory 应该通过编译器为 lambda 生成的 invokedynamic 指令调用。在那种情况下,VM 将始终将调用者的 MethodHandles.lookup() 传递给该方法,因此 Lookup 将始终是 PRIVATE。如果您的用例需要它,直接在 Java 代码中使用 LambdaMetafactory 是非常好的,但这不是主要用途,因此 API 被限制为提供足够的invokedynamic 大小写,仅此而已(换句话说 - 如果 invokedynamicPRIVATE 一起使用,那么让我们禁止其他一切以防万一)。

, the lookup modes must include the private access所述,被LambdaMetaFactory接受。基本上,这意味着调用者 指定的 class,因为它创建了特定的查找实例,或者查找 class 有足够的信任来传递lookup 对象指向执行实际调用的代码(例如,当使用 invokedynamic 指向特定的 bootstrap 方法时暗示)。

从 Java 9 开始,有一种方法 privateLookupIn(Class, MethodHandles.Lookup) 可以尝试通过 private 访问另一个 class 来获取查找对象。正如文档指定的那样,根据模块访问规则检查访问,即调用者必须“允许在目标 class 上进行 深度反射 ”。因此,就模块可访问性而言,上述信任的存在仍然是必需的。我想,这就是框架的方式,框架管理的代码将向框架开放以支持这种访问。

如果那不可行, 包含替代方案,以防您是 class 加载程序的创建者。它使用 class 加载器的 API 来注入一个新的 class 来创建查找对象并允许创建者访问它。有一些可以想象的变化,包括通过让合成 class 回调创建者的代码来移交查找对象而不是将其存储在每个人都可以读取的字段中来确保安全。