Java MethodHandle常量是否可以绕过访问控制?

Java MethodHandle常量是否可以绕过访问控制?,java,methodhandle,Java,Methodhandle,我正在使用JDK15。我正在使用ByteBuddy 1.10.16生成一些类,但我认为除了作为背景信息之外,它在这里基本上是无关的 在其中一个生成的类中,我对一个MethodHandle常量调用invokeExact,该常量是我设法存储在生成的类中的。它是通过获取的字段设置器 在接下来的内容中,我了解了该方法 我注意到,当字段setter MethodHandle表示私有字段时,它会失败。在大多数级别上,这并不让我感到惊讶:直接方法句柄是直接的:虽然我并不假装对所有这些东西的内部了解很多,但在我

我正在使用JDK15。我正在使用ByteBuddy 1.10.16生成一些类,但我认为除了作为背景信息之外,它在这里基本上是无关的

在其中一个生成的类中,我对一个MethodHandle常量调用invokeExact,该常量是我设法存储在生成的类中的。它是通过获取的字段设置器

在接下来的内容中,我了解了该方法

我注意到,当字段setter MethodHandle表示私有字段时,它会失败。在大多数级别上,这并不让我感到惊讶:直接方法句柄是直接的:虽然我并不假装对所有这些东西的内部了解很多,但在我看来,它肯定必须只包装一些没有访问检查的低级字节码

但是考虑到privateLookupIn的存在,它表明在某些情况下绕过访问检查是可能的,那么有没有一条路径可以让我从类a中获取一个字段设置器MethodHandle,该类可以读取一个私有字段,然后将其作为常量存储在另一个类B中,这样就可以成功调用它

我相信我在过去也做过类似的事情,我必须检查涉及私有方法的情况,但在这些情况下,我没有使用MethodHandle常量,也就是说,我在类初始化时使用privateLookupIn获取MethodHandle,并将生成的MethodHandle存储在私有静态最终字段中,然后对该字段的内容调用invokeExact。如果我必须继续走这条路,我会的,但是MethodHandle常量在这里似乎很吸引人,如果可以的话,使用它们会很好

所以我的问题的另一种表述方式是:MethodHandle表示的常量形式是否能够存储其特权?或者是否有某种一次性的方法来提高作为常量存储的MethodHandle的权限?或者,给定的MethodHandle存储为常量,这一事实是否会阻止它始终访问除常规可访问Java构造之外的任何东西

各州:

要解析MH,使用以下四个步骤解析MH字节码行为中对类、接口、字段和方法的所有符号引用:

R已解决。当MH的字节码行为为种类1、2、3或4时,这就像通过字段解析发生一样;当MH的字节码行为为种类5、6、7或8时,这就像通过方法解析发生一样;当MH的字节码行为为种类9时,这就像通过接口方法解析发生一样

链接的章节(即字段)描述了普通解析过程,包括访问控制。即使没有这个明确的语句,您也可以从前面的描述中推导出访问控制的存在性,即这些符号方法句柄引用应该与特定列出的字节码行为等效

因此,通过类文件常量池的常量_MethodHandle _info条目获取的直接方法句柄无法访问字节码指令也无法直接访问的类或成员

但自从JDK 11以来,您可以使用加载由任意引导过程定义的任意类型的常量。因此,当您可以用Java代码来表示如何获取常量时,就像使用privateLookupIn一样,您还可以将其定义为动态常量的引导,并在您可能加载direct方法句柄的地方加载该常量

考虑以下出发点:

公共类动态常数{ 私有静态无效不可访问方法{ 名为.printStackTrace的新ExceptionAssibleMethod; } public static void mainString[]args抛出可丢弃的{ //表示常数 Handle theHandle=新HandleH_invokestic, Type.getInternalNamedConconstant.class,InAccessibleMethod, Type.getMethodDescriptorType.VOID\u类型,false; 字符串生成的类名 =dynconconstant.class.getPackageName.replace'.','/'+/Test; ClassWriter cw=新的ClassWriter0; cw.visit55,ACC|U接口| ACC|U摘要, generatedClassName,null,java/lang/Object,null; MethodVisitor mv=cw.visitMethod ACC|U PUBLIC|ACC|U STATIC,test,V,null,null; mv.visitCode; Visitldcinsenthehandle mv; mv.visitMethodInVokeVirtual, java/lang/invoke/MethodHandle,invokeExact,V,false; mv.visitinsreturn; mv.visitMaxs1,0; visitEnd mv; cw.visitEnd; 字节[]代码=cw.toByteArray; ToolProvider.findFirstjavap.ifpresentorelesejavap->{ 字符串fName=generatedClassName+.class; 试一试{ Path dir=Files.createTempDirectoryjavapTmp; 路径classFile=dir.resolvefName; Files.createDirectoriesclassFile.getParent; Files.writeclassFile,代码; javap.runSystem.out,System.err,-c,-cp, dir.toAbsolutePath.toString,generatedClassName; forPath p=classFile;;p=p.getParent{ Files.deletep; 如果p.equalsdir中断; } }捕捉异常{ 抛出新的未选中的ExceptionEx; } },->System.out.printlnjavap在当前环境中找不到; 试一试{ MethodHandles.Lookup Lookup=MethodHandles.Lookup; lookup.findStaticlookup.defineClasscode, 测试,MethodType.methodTypevoid.class.invokeExact; } 捕集器{ t、 打印跟踪; } } } 它尝试定义一个新的运行时类,该类尝试通过常量\u MethodHandle\u info加载指向InAccessableMethod的MethodHandle常量。程序打印

接口示例.测试{ 公共静态空隙试验; 代码: 0:ldc 12//MethodHandle REF_invokeStatic instexamples/dynConConstant.InAccessibleMethod:V 2:invokevirtual 17//Method java/lang/invoke/MethodHandle.invokeExact:V 5:返回 } java.lang.IllegalAccessError:class instexamples.Test试图访问私有方法“void instexamples.dynConConstant.InAccessableMethod”instexamples.Test和instexamples.dynConConstant位于加载器“app”的未命名模块中 位于instexamples.Test.testUnknown Source 在instexamples.DynConstant.mainDynConstant.java:100 现在,让我们将常数更改为动态常数,它将执行与

MethodHandles.Lookup l=MethodHandles.Lookup; l=MethodHandles.privateLookupInDynConstant.class,l; MethodHandle mh=l.findStatic DYNConConstant.class,InAccessibleMethod,MethodType.methodTypevoid.class; 第一次解析常数时。常数的定义“有点”复杂。由于代码包含三个方法调用,因此定义需要三个方法句柄,此外,还需要现有引导方法的另一个句柄,该句柄允许使用任意方法调用进行引导。这些句柄可用于定义动态常量,而动态常量允许作为另一个动态常量的常量输入

因此,我们将//express常量注释后面的定义替换为:

Type string=Type.getTypeString.class,clazz=Type.getTypeClass.class; 类型oArray=Type.getTypeObject[].class,对象=oArray.getElementType; 类型mhLookup=Type.getTypeMethodHandles.Lookup.class; 类型mHandle=Type.getTypeMethodHandle.class,mType=Type.getTypeMethodType.class; 类型targetType=Type.getTypeDynConstant.class; String methodHandles=Type.getInternalNameMethodHandles.class; Handle methodHandlesLookup=新HandleH_INVOKESTATIC,methodHandles, 查找,Type.getMethodDescriptormhLookup,false; Handle privateLookupIn=新HandleH_INVOKESTATIC,methodHandles, privateLookupIn,Type.getMethodDescriptormhLookup,clazz,mhLookup,false; Handle findStatic=new HandleH_INVOKEVIRTUAL,mhLookup.getInternalName, findStatic,Type.getMethodDescriptormHandle,clazz,string,mType,false; Handle invoke=new HandleH\u invokestic, Type.getInternalNameConstantBootstraps.class,调用, Type.getMethodDescriptorobject、mhLookup、string、clazz、mHandle、oArray、false; ConstantDynamic methodHandlesLookupC=新的ConstantDynamiclookup, mhLookup.getDescriptor,invoke,methodHandlesLookup; ConstantDynamic privateLookupInC=新ConstantDynamic Privatelookupin, mhLookup.getDescriptor、invoke、privateLookupIn、targetType、methodHandlesLookupC; ConstantDynamic theHandle=新ConstantDynamicfindStatic, mHandle.getDescriptor,调用,findStatic, privateLookupInC,targetType,不可访问方法,Type.getMethodTypeV; 为了避免重复非常长的常量方法描述符字符串,我使用ASM的抽象。原则上,我们可以对所有类型名和签名使用常量字符串

此程序打印:

接口示例.测试{ 公共静态空隙试验; 代码: 0:ldc 45//Dynamic 2:findStatic:Ljava/lang/invoke/MethodHandle; 2:invokevirtual 50//Method java/lang/invoke/MethodHandle.invokeExact:V 5:返回 } java.lang.Exception:调用了InAccessibleMethod 在instexamples.dynConConstant.InAccessibleMethodDynConstant.java:23 位于instexamples.Test.testUnknown Source 在instexamples.DynConstant.mainDynConstant.java:89 由方法调用创建的三个常量组成的动态常量的复杂性将导致相当大的常量池。我们可能会生成一个自定义引导方法,并得到一个更小的类文件,尽管我们还有一个额外的方法:

公共类动态常数{ 私有静态无效不可访问方法{ 名为.printStackTrace的新ExceptionAssibleMethod; } 聚氨基甲酸酯 blic静态无效字符串[]args抛出可丢弃{ Type string=Type.getTypeString.class,clazz=Type.getTypeClass.class; 类型mhLookup=Type.getTypeMethodHandles.Lookup.class; 类型mHandle=Type.getTypeMethodHandle.class,mType=Type.getTypeMethodType.class; 类型targetType=Type.getTypeDynConstant.class; 字符串myBootstrapName=privateLookup; 字符串myBootstrapDesc=Type.getMethodDescriptormHandle,mhLookup,String,clazz,clazz,mType; String generatedClassName=dynConConstant.class.getPackageName.replace'.','/'+/Test; Handle myBootStrap=new HandleH_INVOKESTATIC,generatedClassName, myBootstrapName,myBootstrapDesc,true; ConstantDynamic theHandle=新的ConstantDynamicInAccessibleMethod, mHandle.getDescriptor,myBootStrap,targetType,Type.getMethodTypeV; ClassWriter cw=新的ClassWriter0; cw.visit55,ACC|u INTERFACE | ACC|u ABSTRACT,generatedClassName,null,java/lang/Object,null; MethodVisitor mv=cw.VisitMethodAc|u PUBLIC|ACC|u STATIC,test,V,null,null; mv.visitCode; Visitldcinsenthehandle mv; mv.visitMethodInVokeVirtual,java/lang/invoke/MethodHandle,invokeExact,V,false; mv.visitinsreturn; mv.visitMaxs1,0; visitEnd mv; mv=cw.visitMethodac|u PRIVATE|ACC|u STATIC,myBootstrapName,myBootstrapDesc,null,null; mv.visitCode; mv.visitVarInsnALOAD,3;//引导参数,即dynconconstant.class mv.visitVarInsnALOAD,0;//MethodHandles.lookup生成为JVM参数 mv.visitMethodInVokeStatic、java/lang/invoke/MethodHandles、privateLookupIn、, Type.getMethodDescriptormhLookup,clazz,mhLookup,false; mv.visitVarInsnALOAD,3;//引导参数,即dynconconstant.class mv.visitVarInsnALOAD,1;//调用的名称,即InAccessibleMethod mv.visitVarInsnALOAD,4;//引导参数,即MethodType V mv.visitMethodInVokeVirtual,java/lang/invoke/MethodHandles$Lookup,findStatic, Type.getMethodDescriptormHandle,clazz,string,mType,false; mv.visitInsnARETURN; mv.VISITMAX4,5; visitEnd mv; cw.visitEnd; 字节[]代码=cw.toByteArray; ToolProvider.findFirstjavap.ifpresentorelesejavap->{ 字符串fName=generatedClassName+.class; 试一试{ Path dir=Files.createTempDirectoryjavapTmp; 路径classFile=dir.resolvefName; Files.createDirectoriesclassFile.getParent; Files.writeclassFile,代码; javap.runSystem.out,System.err,-p,-c,-cp, dir.toAbsolutePath.toString,generatedClassName; forPath p=classFile;;p=p.getParent{ Files.deletep; 如果p.equalsdir中断; } }捕捉异常{ 抛出新的未选中的ExceptionEx; } },->System.out.printlnjavap在当前环境中找不到; 试一试{ MethodHandles.Lookup Lookup=MethodHandles.Lookup; lookup.findStaticlookup.defineClasscode, 测试,MethodType.methodTypevoid.class.invokeExact; } 捕集器{ t、 打印跟踪; } } } 接口instexamples.custombootstrap.Test{ 公共静态空隙试验; 代码: 0:ldc 18//Dynamic 0:InAccessibleMethod:Ljava/lang/invoke/MethodHandle; 2:invokeVirtual23//methodjava/lang/invoke/MethodHandle.invokeExact:V 5:返回 私有静态java.lang.invoke.MethodHandle privateLookupjava.lang.invoke.MethodHandles$Lookup、java.lang.String、java.lang.Class、java.lang.invoke.MethodType; 代码: 0:aload_3 1:aload_0 2:invokestatic 29//Method java/lang/invoke/MethodHandles.privateLookuppin:Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/invoke/MethodHandles$Lookup; 5:aload_3 6:aload_1 7:aload 4 9:invokevirtual 35//Method java/lang/invoke/MethodHandles$Lookup.findStatic:Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle; 12:轮到你了 } java.lang.Exception:调用了InAccessibleMethod 在instexamples.custombootstrap.dynConConstant.InAccessableMethodDynConstant.java:22 在instexamples.custombootstrap.Test.testUnknown Source上 在instexamples.custombootstrap.dynconconstant.mainDynConstant.java:91 引导方法被设计为可重用的。它以常量参数的形式接收所有必要的信息,因此不同的ldc指令可以使用它来获取不同成员的句柄。JVM已经将调用方的查找上下文作为第一个参数传递,因此我们可以使用它,而不需要调用MethodHandles.lookup。这门课很有趣 成员的arch是第一个附加参数,它用作privateLookupIn和findStatic的第一个参数。因为每个动态常量都有一个标准的name参数,所以我们可以使用它来表示成员的名称。最后一个参数表示方法要查找的MethodType。当我们为字段查找改进此参数时,我们可以删除该参数,作为第三个标准参数,预期常量类型可以与预期字段的类型匹配

基本上,自定义引导方法会执行您在问题中描述的基于privateLookupIn的查找,但将其与ldc一起使用可以进行延迟初始化,而不是静态最终字段的类初始化时间,同时在链接指令后仍会像静态最终字段一样进行优化。此外,允许将这些动态常量作为其他引导方法的常量输入,用于其他动态常量或InvokedDynamic指令。不过,您也可以使用以下状态将现有静态最终字段调整为动态常量:

要解析MH,使用以下四个步骤解析MH字节码行为中对类、接口、字段和方法的所有符号引用:

R已解决。当MH的字节码行为为种类1、2、3或4时,这就像通过字段解析发生一样;当MH的字节码行为为种类5、6、7或8时,这就像通过方法解析发生一样;当MH的字节码行为为种类9时,这就像通过接口方法解析发生一样

链接的章节(即字段)描述了普通解析过程,包括访问控制。即使没有这个明确的语句,您也可以从前面的描述中推导出访问控制的存在性,即这些符号方法句柄引用应该与特定列出的字节码行为等效

因此,通过类文件常量池的常量_MethodHandle _info条目获取的直接方法句柄无法访问字节码指令也无法直接访问的类或成员

但自从JDK 11以来,您可以使用加载由任意引导过程定义的任意类型的常量。因此,当您可以用Java代码来表示如何获取常量时,就像使用privateLookupIn一样,您还可以将其定义为动态常量的引导,并在您可能加载direct方法句柄的地方加载该常量

考虑以下出发点:

公共类动态常数{ 私有静态无效不可访问方法{ 名为.printStackTrace的新ExceptionAssibleMethod; } public static void mainString[]args抛出可丢弃的{ //表示常数 Handle theHandle=新HandleH_invokestic, Type.getInternalNamedConconstant.class,InAccessibleMethod, Type.getMethodDescriptorType.VOID\u类型,false; 字符串生成的类名 =dynconconstant.class.getPackageName.replace'.','/'+/Test; ClassWriter cw=新的ClassWriter0; cw.visit55,ACC|U接口| ACC|U摘要, generatedClassName,null,java/lang/Object,null; MethodVisitor mv=cw.visitMethod ACC|U PUBLIC|ACC|U STATIC,test,V,null,null; mv.visitCode; Visitldcinsenthehandle mv; mv.visitMethodInVokeVirtual, java/lang/invoke/MethodHandle,invokeExact,V,false; mv.visitinsreturn; mv.visitMaxs1,0; visitEnd mv; cw.visitEnd; 字节[]代码=cw.toByteArray; ToolProvider.findFirstjavap.ifpresentorelesejavap->{ 字符串fName=generatedClassName+.class; 试一试{ Path dir=Files.createTempDirectoryjavapTmp; 路径classFile=dir.resolvefName; Files.createDirectoriesclassFile.getParent; Files.writeclassFile,代码; javap.runSystem.out,System.err,-c,-cp, dir.toAbsolutePath.toString,generatedClassName; forPath p=classFile;;p=p.getParent{ Files.deletep; 如果p.equalsdir中断; } }捕捉异常{ 抛出新的未选中的ExceptionEx; } },->System.out.printlnjavap在当前环境中找不到; 试一试{ MethodHandles.Lookup Lookup=MethodHandles.Lookup; lookup.findStaticlookup.defineClasscode, 测试,MethodType.methodTypevoid.class.invokeExact; } 捕集器{ t、 打印跟踪; } } } 它尝试定义一个新的运行时类,该类尝试通过常量\u MethodHandle\u info加载指向InAccessableMethod的MethodHandle常量。程序打印

接口示例.测试{ 公共静态空隙试验; 代码: 0:ldc 12//MethodHandle REF_invokeStatic ins t样本/动态恒定不可测方法:V 2:invokevirtual 17//Method java/lang/invoke/MethodHandle.invokeExact:V 5:返回 } java.lang.IllegalAccessError:class instexamples.Test试图访问私有方法“void instexamples.dynConConstant.InAccessableMethod”instexamples.Test和instexamples.dynConConstant位于加载器“app”的未命名模块中 位于instexamples.Test.testUnknown Source 在instexamples.DynConstant.mainDynConstant.java:100 现在,让我们将常数更改为动态常数,它将执行与

MethodHandles.Lookup l=MethodHandles.Lookup; l=MethodHandles.privateLookupInDynConstant.class,l; MethodHandle mh=l.findStatic DYNConConstant.class,InAccessibleMethod,MethodType.methodTypevoid.class; 第一次解析常数时。常数的定义“有点”复杂。由于代码包含三个方法调用,因此定义需要三个方法句柄,此外,还需要现有引导方法的另一个句柄,该句柄允许使用任意方法调用进行引导。这些句柄可用于定义动态常量,而动态常量允许作为另一个动态常量的常量输入

因此,我们将//express常量注释后面的定义替换为:

Type string=Type.getTypeString.class,clazz=Type.getTypeClass.class; 类型oArray=Type.getTypeObject[].class,对象=oArray.getElementType; 类型mhLookup=Type.getTypeMethodHandles.Lookup.class; 类型mHandle=Type.getTypeMethodHandle.class,mType=Type.getTypeMethodType.class; 类型targetType=Type.getTypeDynConstant.class; String methodHandles=Type.getInternalNameMethodHandles.class; Handle methodHandlesLookup=新HandleH_INVOKESTATIC,methodHandles, 查找,Type.getMethodDescriptormhLookup,false; Handle privateLookupIn=新HandleH_INVOKESTATIC,methodHandles, privateLookupIn,Type.getMethodDescriptormhLookup,clazz,mhLookup,false; Handle findStatic=new HandleH_INVOKEVIRTUAL,mhLookup.getInternalName, findStatic,Type.getMethodDescriptormHandle,clazz,string,mType,false; Handle invoke=new HandleH\u invokestic, Type.getInternalNameConstantBootstraps.class,调用, Type.getMethodDescriptorobject、mhLookup、string、clazz、mHandle、oArray、false; ConstantDynamic methodHandlesLookupC=新的ConstantDynamiclookup, mhLookup.getDescriptor,invoke,methodHandlesLookup; ConstantDynamic privateLookupInC=新ConstantDynamic Privatelookupin, mhLookup.getDescriptor、invoke、privateLookupIn、targetType、methodHandlesLookupC; ConstantDynamic theHandle=新ConstantDynamicfindStatic, mHandle.getDescriptor,调用,findStatic, privateLookupInC,targetType,不可访问方法,Type.getMethodTypeV; 为了避免重复非常长的常量方法描述符字符串,我使用ASM的抽象。原则上,我们可以对所有类型名和签名使用常量字符串

此程序打印:

接口示例.测试{ 公共静态空隙试验; 代码: 0:ldc 45//Dynamic 2:findStatic:Ljava/lang/invoke/MethodHandle; 2:invokevirtual 50//Method java/lang/invoke/MethodHandle.invokeExact:V 5:返回 } java.lang.Exception:调用了InAccessibleMethod 在instexamples.dynConConstant.InAccessibleMethodDynConstant.java:23 位于instexamples.Test.testUnknown Source 在instexamples.DynConstant.mainDynConstant.java:89 由方法调用创建的三个常量组成的动态常量的复杂性将导致相当大的常量池。我们可能会生成一个自定义引导方法,并得到一个更小的类文件,尽管我们还有一个额外的方法:

公共类动态常数{ 私有静态无效不可访问方法{ 名为.printStackTrace的新ExceptionAssibleMethod; } public static void mainString[]args抛出可丢弃的{ Type string=Type.getTypeString.class,clazz=Type.getTypeClass.class; 类型mhLookup=Type.getTypeMethodHandles.Lookup.class; 类型mHandle=Type.getTypeMethodHandle.class,mType=Type.getTypeMethodType.class; 类型targetType=Type.getTypeDynConstant.class; 字符串myBootstrapName=privateLookup; 字符串myBootstrapDesc=Type.getMethodDescriptormHandle,mhLookup,String,clazz,clazz,mType; String generatedClassName=dynConConstant.class.getPackageName.replace'.','/'+/Test; Handle myBootStrap=new HandleH_INVOKESTATIC,generatedClassName, myBootstrapName,myBootstrapDesc,true; ConstantDynamic theHandle=新的ConstantDynamicInAccessibleMethod, mHandle.getDescriptor,myBootStrap,targetType,Type.getMethodTypeV; ClassWriter cw=新的ClassWriter0; cw.visit55,ACC|u INTERFACE | ACC|u ABSTRACT,generatedClassName,null,java/lang/Object,null; MethodVisitor mv= cw.VisitMethodac|u PUBLIC|ACC|u STATIC,test,V,null,null; mv.visitCode; Visitldcinsenthehandle mv; mv.visitMethodInVokeVirtual,java/lang/invoke/MethodHandle,invokeExact,V,false; mv.visitinsreturn; mv.visitMaxs1,0; visitEnd mv; mv=cw.visitMethodac|u PRIVATE|ACC|u STATIC,myBootstrapName,myBootstrapDesc,null,null; mv.visitCode; mv.visitVarInsnALOAD,3;//引导参数,即dynconconstant.class mv.visitVarInsnALOAD,0;//MethodHandles.lookup生成为JVM参数 mv.visitMethodInVokeStatic、java/lang/invoke/MethodHandles、privateLookupIn、, Type.getMethodDescriptormhLookup,clazz,mhLookup,false; mv.visitVarInsnALOAD,3;//引导参数,即dynconconstant.class mv.visitVarInsnALOAD,1;//调用的名称,即InAccessibleMethod mv.visitVarInsnALOAD,4;//引导参数,即MethodType V mv.visitMethodInVokeVirtual,java/lang/invoke/MethodHandles$Lookup,findStatic, Type.getMethodDescriptormHandle,clazz,string,mType,false; mv.visitInsnARETURN; mv.VISITMAX4,5; visitEnd mv; cw.visitEnd; 字节[]代码=cw.toByteArray; ToolProvider.findFirstjavap.ifpresentorelesejavap->{ 字符串fName=generatedClassName+.class; 试一试{ Path dir=Files.createTempDirectoryjavapTmp; 路径classFile=dir.resolvefName; Files.createDirectoriesclassFile.getParent; Files.writeclassFile,代码; javap.runSystem.out,System.err,-p,-c,-cp, dir.toAbsolutePath.toString,generatedClassName; forPath p=classFile;;p=p.getParent{ Files.deletep; 如果p.equalsdir中断; } }捕捉异常{ 抛出新的未选中的ExceptionEx; } },->System.out.printlnjavap在当前环境中找不到; 试一试{ MethodHandles.Lookup Lookup=MethodHandles.Lookup; lookup.findStaticlookup.defineClasscode, 测试,MethodType.methodTypevoid.class.invokeExact; } 捕集器{ t、 打印跟踪; } } } 接口instexamples.custombootstrap.Test{ 公共静态空隙试验; 代码: 0:ldc 18//Dynamic 0:InAccessibleMethod:Ljava/lang/invoke/MethodHandle; 2:invokeVirtual23//methodjava/lang/invoke/MethodHandle.invokeExact:V 5:返回 私有静态java.lang.invoke.MethodHandle privateLookupjava.lang.invoke.MethodHandles$Lookup、java.lang.String、java.lang.Class、java.lang.invoke.MethodType; 代码: 0:aload_3 1:aload_0 2:invokestatic 29//Method java/lang/invoke/MethodHandles.privateLookuppin:Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/invoke/MethodHandles$Lookup; 5:aload_3 6:aload_1 7:aload 4 9:invokevirtual 35//Method java/lang/invoke/MethodHandles$Lookup.findStatic:Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle; 12:轮到你了 } java.lang.Exception:调用了InAccessibleMethod 在instexamples.custombootstrap.dynConConstant.InAccessableMethodDynConstant.java:22 在instexamples.custombootstrap.Test.testUnknown Source上 在instexamples.custombootstrap.dynconconstant.mainDynConstant.java:91 引导方法被设计为可重用的。它以常量参数的形式接收所有必要的信息,因此不同的ldc指令可以使用它来获取不同成员的句柄。JVM已经将调用方的查找上下文作为第一个参数传递,因此我们可以使用它,而不需要调用MethodHandles.lookup。要搜索成员的类是第一个附加参数,它用作privateLookupIn和findStatic的第一个参数。因为每个动态常量都有一个标准的name参数,所以我们可以使用它来表示成员的名称。最后一个参数表示方法要查找的MethodType。当我们为字段查找改进此参数时,我们可以删除该参数,作为第三个标准参数,预期常量类型可以与预期字段的类型匹配

基本上,自定义引导方法会执行您在问题中描述的基于privateLookupIn的查找,但将其与ldc一起使用可以进行延迟初始化,而不是静态最终字段的类初始化时间,同时在链接指令后仍会像静态最终字段一样进行优化。此外,允许将这些动态常量作为常量输入到其他引导方法,用于其他动态常量或invokedynamic指令。不过,您也可以使用
g.

我不知道ByteBuddy的具体情况,但通常在解析常量时执行访问检查。您可以通过预先解析MethodHandle,然后使用常量池修补将活动对象放入常量池来解决这个问题,但是目前没有可用的公共API来实现这一点,尽管我相信在was/is计划中:。@jornverne Afaik,没有计划将持续池修补引入公共API,因为它应该取代旧的Unsafe.defineAnonymousClass,持续池修补将不需要更换而消失。您可以使用动态常量,通过ldc指令或类似指令加载这样的方法句柄。不过,与无状态最终字段相比没有任何好处。@Holger注意到,还有一种方法,目前是package private,可以用来替代持续的池修补。虽然你是对的,但它们并不完全相同。这是取代CP补丁的计划,但我最近没有问过……我不知道ByteBuddy的具体情况,但一般来说,访问检查是在常量解析后执行的。您可以通过预先解析MethodHandle,然后使用常量池修补将活动对象放入常量池来解决这个问题,但是目前没有可用的公共API来实现这一点,尽管我相信在was/is计划中:。@jornverne Afaik,没有计划将持续池修补引入公共API,因为它应该取代旧的Unsafe.defineAnonymousClass,持续池修补将不需要更换而消失。您可以使用动态常量,通过ldc指令或类似指令加载这样的方法句柄。不过,与无状态最终字段相比没有任何好处。@Holger注意到,还有一种方法,目前是package private,可以用来替代持续的池修补。虽然你是对的,但它们并不完全相同。这是取代CP补丁的计划,但我最近没有问过……谢谢你;这正是我不久前采用的方法。它确实非常强大。在您的示例中,我只需将引导方法置于DYNConconstant中—使其更具可读性。@JohannesKuhn依赖现有引导方法是否可行取决于实际用例。例如,重要的是引导方法与目标方法不在同一个类中,以演示如何获取不可访问方法的句柄。还有其他可以想象的场景,声明类包含一个引导方法,允许某些朋友类以这种方式访问它们……很少需要用字节码编写实际的引导类。是的,如果您将引导类移动到DynConConstant,它可以在您的示例中使用自己的查找。但这不是重点——重点是:用普通java编写引导方法可能会更容易。我不想预测将来更可能使用什么。我试图给出一个完整的画面。由于答案解释了bootstrap方法的原理,也提供了普通的java等价物,因此应该有足够的信息,允许读者使用他们喜欢的方法编写自己的bootstrap方法;这正是我不久前采用的方法。它确实非常强大。在您的示例中,我只需将引导方法置于DYNConconstant中—使其更具可读性。@JohannesKuhn依赖现有引导方法是否可行取决于实际用例。例如,重要的是引导方法与目标方法不在同一个类中,以演示如何获取不可访问方法的句柄。还有其他可以想象的场景,声明类包含一个引导方法,允许某些朋友类以这种方式访问它们……很少需要用字节码编写实际的引导类。是的,如果您将引导类移动到DynConConstant,它可以在您的示例中使用自己的查找。但这不是重点——重点是:用普通java编写引导方法可能会更容易。我不想预测将来更可能使用什么。我试图给出一个完整的画面。由于答案解释了bootstrap方法的原理,并提供了与普通java等效的方法,因此应该有足够的信息,允许读者使用他们喜欢的方法编写自己的bootstrap方法。