LambdaMetafactory访问另一个ClassLoader上的类

时间:2018-01-25 11:20:14

标签: java lambda methodhandle lambda-metafactory

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

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加载的类的方法,则会抛出:

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

2 个答案:

答案 0 :(得分:1)

LambdaMetafactory方法要求您将PRIVATE次查询作为其第一个参数传递。规范caller - Represents a lookup context with the accessibility privileges of the caller.可能有点模糊,但它应该提供关于Lookup所需特权的想法。

为什么它必须是私有的?这就是API的未来证明。请记住,LambdaMetafactory应该通过编译器为lambdas生成的invokedynamic指令来调用。在这种情况下,VM将始终将呼叫者的MethodHandles.lookup()传递给该方法,因此Lookup始终为PRIVATE。直接在Java代码中使用LambdaMetafactory非常好,如果您需要它用于您的用例,但它不是主要用途,因此API仅限于为invokedynamic情况提供足够的空间但仅此而已(换句话说 - 如果invokedynamicPRIVATE一起使用,那么就以其他方式禁止其他所有内容。)

答案 1 :(得分:1)

正如this answer所述,lookup modes必须包含LambdaMetaFactory要接受的private access。基本上,这意味着调用者指定的类,因为它创建了特定的查找实例,或者查找类有足够的信任将查找对象传递给执行实际调用的代码(例如,当使用指向特定引导方法的invokedynamic时暗示。)

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

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