栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

使用LambdaMetafactory对从其他类加载器获得的类实例调用one-arg方法

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用LambdaMetafactory对从其他类加载器获得的类实例调用one-arg方法

MethodHandles.Lookup
返回的实例
MethodHandles.lookup()
封装了调用者的上下文,即创建新类加载器的类的上下文。如异常所示,该类型
Formatter
在此上下文中不可见。您可以将其看作是模仿操作的编译时语义的尝试。如果将语句放置
Formatter.formatSource(sourceText)
在代码中,由于该类型不在范围内,那么它将无法正常工作。

您可以使用更改查找对象的上下文类

in(Class)
,但是使用时
MethodHandles.lookup().in(formatterClass)
,您会遇到其他问题。更改查找对象的上下文类将降低访问级别,以使其与Java访问规则保持一致,即,您只能访问
public
该类的成员
Formatter
。但是,
Lambdametafactory
唯一接受有权
private
访问其查找类的查找对象,即由调用者本身直接生成的查找对象。唯一的例外是在嵌套类之间进行更改。

因此,在中使用

MethodHandles.lookup().in(formatterClass)
results
Invalid caller:com.google.googlejavaformat.java.Formatter
,因为您(调用方)不是
Formatter
该类。或者从技术上讲,查找对象没有
private
访问模式。

Java API不提供任何(简单)方式来使查找对象位于不同的类加载上下文中并具有

private
访问权限(在Java
9之前)。所有常规机制都将涉及驻留在该上下文中的代码的合作。这就是开发人员经常采用带有访问覆盖的反射功能来操纵查找对象以具有所需属性的方法。不幸的是,新的模块系统有望在未来变得更加严格,可能会破坏这些解决方案。

Java
9提供了一种获取此类查找对象的方法,

privateLookupIn
该方法要求目标类位于同一模块中,或者将其模块开放给调用者的模块以允许这种访问。

由于创建的是new

ClassLoader
,因此可以使用类加载上下文。因此,解决问题的一种方法是向其添加另一个类,该类创建查找对象并允许您的调用代码检索它:

    try (URLClassLoader cl = new URLClassLoader(urls.toArray(new URL[0])) {        { byte[] pre = gimmeLookupClassDef();          defineClass("GimmeLookup", pre, 0, pre.length); }  }) {        MethodHandles.Lookup lookup = (MethodHandles.Lookup) cl.loadClass("GimmeLookup").getField("lookup").get(null);        Class<?> formatterClass = cl.loadClass("com.google.googlejavaformat.java.Formatter");        Object formatInstance = formatterClass.getConstructor().newInstance();        Method method = formatterClass.getMethod("formatSource", String.class);        MethodHandle methodHandle = lookup.unreflect(method);        MethodType type = methodHandle.type();        MethodType factoryType = MethodType.methodType(FormatInvoker.class, type.parameterType(0));        type = type.dropParameterTypes(0, 1);        FormatInvoker formatInvoker = (FormatInvoker)          Lambdametafactory.metafactory(     lookup, "invoke", factoryType, type, methodHandle, type) .getTarget().invoke(formatInstance);      String text = (String) formatInvoker.invoke(sourceText);      System.out.println(text);    }static byte[] gimmeLookupClassDef() {    return ( "u00CAu00FEu00BAu00BE00121113GimmeLookup71120"    +"java/lang/Object73110<clinit>13()V14Code16lookup1'Ljav"    +"a/lang/invoke/MethodHandles$Lookup;141011112121)()Ljava/lang"    +"/invoke/MethodHandles$Lookup;136java/lang/invoke/MethodHandles71514"    +"101412161726124120311011120115"    +"61723337u00B820u00B313u00B1" )    .getBytes(StandardCharsets.ISO_8859_1);}

该子类在构造函数中

URLClassLoader
调用
defineClass
一次,以添加一个等效于

public interface GimmeLookup {    MethodHandles.Lookup lookup = MethodHandles.lookup();}

然后,代码

lookup
通过反射读取该字段。查找对象封装的上下文中
GimmeLookup
,这是内新定义的
URLClassLoader
,并且足以以访问
public
方法
formatSource
public

com.google.googlejavaformat.java.Formatter

该接口

FormatInvoker
可用于该上下文,因为您代码的类加载器将成为created的父级
URLClassLoader


一些附加说明:

  • 当然,如果您

    FormatInvoker
    足够频繁地使用生成的实例来补偿创建它的成本,那么它只能比任何其他反射式访问更有效。

  • 我删除了该

    Thread.currentThread().setContextClassLoader(cl);
    语句,因为它在此操作中没有任何意义,但是实际上,由于您没有将其重新设置,因此非常危险,因此该线程
    URLClassLoader
    此后一直引用关闭的内容。

  • 我简化了对的

    toArray
    呼叫
    urls.toArray(new URL[0])
    。本文提供了一个非常有趣的观点,说明了为数组指定集合大小的有用性。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/438863.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号