Module jdk.dynalink
Dynalink是一个用于对象上高级操作的动态链接库。这些操作包括“读取属性”、“写入属性”、“调用函数”等。Dynalink主要用于实现编程语言,其中至少有一些表达式具有动态类型(即,无法在静态情况下确定的类型),并且动态类型的操作被表达为调用站点。这些调用站点将根据表达式评估为的值的实际类型在运行时链接到适当的目标方法句柄。这些类型在调用之间可能会发生变化,需要多次重新链接调用站点以适应新类型;Dynalink处理所有这些以及更多。
Dynalink支持实现具有与JVM的基于类的模型不同(甚至是根本不同)的对象模型和具有自定义类型转换的编程语言。
Dynalink与java.lang.invoke
包密切相关,并依赖于该包。
虽然java.lang.invoke
提供了一个用于动态链接invokedynamic
调用站点的低级API,但它没有提供一种表达对象上高级操作或实现这些操作的方法。这些操作在面向对象的环境中是通常的:属性访问,访问集合元素,调用方法和构造函数(可能具有多重分派,例如Java重载方法解析的链接和运行时等效)。这些都是在JVM上的语言中通常希望的功能。如果一种语言是静态类型的,并且其类型系统与JVM的匹配,它可以通过使用常规的调用、字段访问等指令(例如invokevirtual
、getfield
)来实现这一点。但是,如果语言是动态的(因此,某些表达式的类型直到运行时才知道),或者其对象模型或类型系统与JVM的不太匹配,则应改用invokedynamic
调用站点,并让Dynalink管理它们。
示例
通过示例展示Dynalink可能是最好的解释方式。假设您有一个程序,使用一种无需声明对象类型的语言,并且要访问其属性:var color = obj.color;如果生成一个Java类来表示上述一行程序,其字节码将类似于:
aload 2 // 将“obj”加载到堆栈上 invokedynamic "GET:PROPERTY:color"(Object)Object // 在未知类型对象上调用属性获取器 astore 3 // 将返回值存储到本地变量“color”为了链接
invokedynamic
指令,我们需要一个引导方法。一个使用Dynalink的最简引导方法可能如下:
import java.lang.invoke.*; import jdk.dynalink.*; import jdk.dynalink.support.*; class MyLanguageRuntime { private static final DynamicLinker dynamicLinker = new DynamicLinkerFactory().createLinker(); public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) { return dynamicLinker.link( new SimpleRelinkableCallSite( new CallSiteDescriptor(lookup, parseOperation(name), type))); } private static Operation parseOperation(String name) { ... } }上述代码片段中有几个重要的对象:
DynamicLinker
是Dynalink中的主要对象,它协调将调用站点链接到实现其中命名操作的方法句柄。它是使用DynamicLinkerFactory
进行配置和创建的。- 当调用引导方法时,需要创建一个
CallSite
对象。在Dynalink中,这些调用站点需要另外实现RelinkableCallSite
接口。这里的“可重新链接”指的是如果调用站点在运行时遇到不同类型的对象,其目标将更改为可以在新遇到的类型上执行操作的方法句柄。SimpleRelinkableCallSite
和ChainedCallSite
(未在上面的示例中使用)是库中已提供的两种实现。 - Dynalink使用
CallSiteDescriptor
对象来保存引导方法的参数:查找和方法类型,因为每当需要重新链接调用站点时,它将需要它们。 - Dynalink使用
Operation
对象来表达动态操作。它不规定您如何在调用站点中编码操作,因此在上面的示例中,parseOperation
函数为空,您应该提供代码来将字符串"GET:PROPERTY:color"
解析为命名属性获取器操作对象,如StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color")
。
通过上述设置,您已经可以做什么?DynamicLinkerFactory
默认创建一个可以链接具有常规Java语义的Java对象的DynamicLinker
。如果您有这三个简单的类:
public class A { public String color; public A(String color) { this.color = color; } } public class B { private String color; public B(String color) { this.color = color; } public String getColor() { return color; } } public class C { private int color; public C(int color) { this.color = color; } public int getColor() { return color; } }并且以某种方式创建它们的实例并将它们传递给您编程语言中的调用站点:
for each(var obj in [new A("red"), new B("green"), new C(0x0000ff)]) { print(obj.color); }那么在第一次调用时,Dynalink将将
.color
获取器操作链接到A.color
的字段获取器,第二次调用时,它将重新链接到B.getColor()
返回一个String
,最后在第三次调用时,它将重新链接到C.getColor()
返回一个int
。我们上面使用的SimpleRelinkableCallSite
仅记住了最后遇到的类型的链接(它实现了所谓的单态内联缓存)。另一个已提供的实现,ChainedCallSite
将记住几种不同类型的链接(它是一个多态内联缓存)并且在严肃的应用程序中可能是更好的选择。
Dynalink和字节码创建
CallSite
对象通常作为字节码中引导invokedynamic
指令的一部分创建。因此,Dynalink通常作为将程序编译为Java.class
字节码格式的语言运行时的一部分使用。Dynalink不涉及创建字节码类或将其加载到JVM中的方面。也就是说,Dynalink也可以在没有字节码编译的情况下使用(例如,在语言解释器中),方法是显式创建CallSite
对象,并将它们与解释程序中动态操作的表示(例如,语法树中的一些节点对象)关联起来。
可用操作
Dynalink在其StandardOperation
类中定义了几种标准操作。Java对象的链接器可以链接所有这些操作,并鼓励您至少支持和使用这些操作。标准操作GET
和SET
需要与至少一个Namespace
结合使用才能发挥作用,例如,要表达属性获取器,您将使用StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY)
。Dynalink在StandardNamespace
类中定义了三个标准命名空间。要将固定名称与操作关联,您可以使用NamedOperation
,如前面示例中所示:StandardOperation.GET.withNamespace(StandardNamespace.PROPERTY).named("color")
表示名为“color”的属性的获取器。
多个命名空间上的操作
一些语言可能在对象上没有单独的命名空间用于属性、元素和方法,源语言构造可能一次性处理其中的几个。Dynalink支持使用Namespace
对象和NamespaceOperation
指定多个。
特定于语言的链接器
定义其自己的对象模型不同于JVM基于类的模型和/或使用自己的类型转换的语言将需要创建自己的特定于语言的链接器。请参阅jdk.dynalink.linker
包,特别是GuardingDynamicLinker
接口以开始使用。
Dynalink和Java对象
由DynamicLinkerFactory
默认创建的DynamicLinker
对象包含一个内部实例BeansLinker
,它是一个实现所有上述操作的常规Java语义的语言特定链接器,并且可以链接任何其他语言特定链接器未能链接的Java对象。这样,所有语言运行时都具有与普通Java对象的内置互操作性。请参见BeansLinker
详细了解它如何链接各种操作。
跨语言互操作性
一个DynamicLinkerFactory
可以配置一个 类加载器。它将尝试实例化所有对该类加载器可见的 GuardingDynamicLinkerExporter
类,并将它们提供的链接器组合到创建的 DynamicLinker
中。这允许不同语言之间的互操作性:如果您在JVM中部署了两种语言运行时A和B,并通过上述机制导出它们的链接器,语言运行时A将在其 DynamicLinker
对象中拥有来自B的特定于语言的链接器实例,反之亦然。这意味着如果从语言运行时B传递对象到语言运行时A的代码,当它遇到来自B的对象时,B的链接器将有机会链接A中的调用点。
-
Packages
ExportsPackageDescription包含用于链接invokedynamic
调用站点的接口和类。包含普通Java对象的链接器。包含语言运行时需要的接口和类,以实现它们自己的特定于语言的对象模型和类型转换。包含使语言运行时更方便地实现其自己的特定于语言的对象模型和类型转换的类,通过提供一些类的基本实现以及各种实用工具。包含使使用Dynalink更方便的类,通过提供一些类的基本实现以及各种实用工具。 -
Services
Uses