了解Javassist中的常量池

了解Javassist中的常量池,java,javassist,Java,Javassist,我正在使用Javassist在运行时扩展某些类。 在一些地方(在生成代码中),我需要创建JavassistConstPool类的实例。 例如,为了将生成的类标记为synthetic,我编写了如下代码: CtClass ctClassToExtend = ... //class to extend CtClass newCtClass = extend(ctClassToExtend, ...); //method to create a new ctClass extending ctClass

我正在使用Javassist在运行时扩展某些类。 在一些地方(在生成代码中),我需要创建Javassist
ConstPool
类的实例。 例如,为了将生成的类标记为
synthetic
,我编写了如下代码:

CtClass ctClassToExtend = ... //class to extend
CtClass newCtClass = extend(ctClassToExtend, ...); //method to create a new ctClass extending ctClassToExtend
SyntheticAttribute syntheticAttribute = new SyntheticAttribute(ctClassToExtend.getClassFile().getConstPool()); //creating a synthetic attribute using an instance of ConstPool
newCtClass.setAttribute(syntheticAttribute.getName(), syntheticAttribute.get()); //marking the generated class as synthetic
这是预期的工作,但我有一定的怀疑这是完全正确的。具体而言,我的主要问题是:

在本例中,调用
CtClass.getClassFile().getConstPool()
是获取常量池的正确方法吗?。如果不是,那么在运行时使用Javassist创建新类时,获取常量池正确实例的一般正确方法是什么

另外,我对这里幕后发生的事情有点茫然:为什么我们需要一个常量池来创建合成属性的实例?或者一般来说,创建任何其他类型的类属性的实例


感谢您的澄清。

不知道您是否对答案感兴趣,但至少可以帮助其他人 找到这个问题

首先,给每个开始创建/修改字节码的人一个小建议 而且需要更多关于JVM内部如何工作的深入信息,一开始可能看起来笨重可怕,但这是非常宝贵的帮助

在本例中,调用CtClass.getClassFile().getConstPool()是否是获取常量池的正确方法

是的。每个Java类都有一个常量池,因此每次需要访问该常量时都会使用basicali 对于给定的类,您可以执行
ctClass.getClassFile().getConstPool()
,但必须记住 以下:

  • 在javassist中,
    CtClass
    中的常量池字段是一个实例字段,这意味着如果您有两个
    CtClass
    对象 表示同一个类时,将有两个不同的常量池实例(即使它们表示 实际类文件中的常量池)。修改一个
    CtClass
    实例时,必须使用 关联的常量池实例,以便具有预期的行为

  • 有时,您可能没有
    CtClass
    ,而是有
    CtMethod
    CtField
    ,它们不允许您回溯到
    CtClass
    实例,在这种情况下,您可以使用
    CtMethod.getMethodInfo().getConstPool()
    CtField.getFieldInfo().getConstPool()
    检索正确的常量池

    既然我已经提到了
    CtMethod
    CtField
    ,请记住,如果要向其中任何一个添加属性,它不能通过
    ClassFile
    对象,而是分别通过
    MethodInfo
    FieldInfo
    来添加

  • 为什么我们需要一个常量池来创建一个合成属性的实例?或者一般来说,创建任何其他类型的类属性的实例

    为了回答这个问题,我将开始引用(如我所说,此文档非常有用):

    Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局。相反,指令引用常量池表中的符号信息

    考虑到这一点,我认为最好的方法就是查看类文件转储。我们可以通过运行以下命令来实现这一点:

    javap -s -c -p -v SomeClassFile.class
    
    Javap随javasdk一起提供,它是在这个级别分析类的一个很好的工具,解释了每个开关

    • -s:打印内部类型签名
    • -打印字节码
    • -p:打印所有类成员(方法和字段,包括私有的)
    • -v:要详细,将打印大头钉信息和类常量池
    下面是我通过javassist修改的
    test.Test1
    类的输出,它在类和
    injectedMethod

    Classfile /C:/development/testProject/test/Test1.class
    Last modified 29/Nov/2012; size 612 bytes
    MD5 checksum 858c009090bfb57d704b2eaf91c2cb75
    Compiled from "Test1.java"
    public class test.Test1
    SourceFile: "Test1.java"
    Synthetic: true
    minor version: 0
    major version: 50
    flags: ACC_PUBLIC, ACC_SUPER
    
    Constant pool:
    #1 = Class              #2             //  test/Test1
    #2 = Utf8               test/Test1
    #3 = Class              #4             //  java/lang/Object
    #4 = Utf8               java/lang/Object
    #5 = Utf8               <init>
    #6 = Utf8               ()V
    #7 = Utf8               Code
    #8 = Methodref          #3.#9          //  java/lang/Object."<init>":()V
    #9 = NameAndType        #5:#6          //  "<init>":()V
    #10 = Utf8               LineNumberTable
    #11 = Utf8               LocalVariableTable
    #12 = Utf8               this
    #13 = Utf8               Ltest/Test1;
    #14 = Utf8               SourceFile
    #15 = Utf8               Test1.java
    #16 = Utf8               someInjectedMethod
    #17 = Utf8               java/lang/System
    #18 = Class              #17            //  java/lang/System
    #19 = Utf8               out
    #20 = Utf8               Ljava/io/PrintStream;
    #21 = NameAndType        #19:#20        //  out:Ljava/io/PrintStream;
    #22 = Fieldref           #18.#21        //  java/lang/System.out:Ljava/io/PrintStream;
    #23 = Utf8               injection example
    #24 = String             #23            //  injection example
    #25 = Utf8               java/io/PrintStream
    #26 = Class              #25            //  java/io/PrintStream
    #27 = Utf8               println
    #28 = Utf8               (Ljava/lang/String;)V
    #29 = NameAndType        #27:#28        //  println:(Ljava/lang/String;)V
    #30 = Methodref          #26.#29        //  java/io/PrintStream.println:(Ljava/lang/String;)V
    #31 = Utf8               RuntimeVisibleAnnotations
    #32 = Utf8               Ltest/TestAnnotationToShowItInConstantTable;
    #33 = Utf8               Synthetic
    {
    public com.qubit.augmentation.test.Test1();
    Signature: ()V
    flags: ACC_PUBLIC
    
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   Ltest/Test1;
    
    protected void someInjectedMethod();
    Signature: ()V
    flags: ACC_PROTECTED
    
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #24                 // String injection example
         5: invokevirtual #30                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return        
    RuntimeVisibleAnnotations:
      0: #32()
    Synthetic: true
    }
    
    Classfile/C:/development/testProject/test/Test1.class
    最后修改日期:2012年11月29日;大小为612字节
    MD5校验和858C0090BFB57D704B2EAF91C2CB75
    从“Test1.java”编译而来
    公共类test.Test1
    源文件:“Test1.java”
    合成的:真的
    次要版本:0
    主要版本:50
    旗帜:ACC_公共、ACC_超级
    固定池:
    #1=类#2//测试/测试1
    #2=Utf8测试/测试1
    #3=类#4//java/lang/Object
    #4=Utf8 java/lang/Object
    #5=Utf8
    #6=Utf8()V
    #7=Utf8代码
    #8=Methodref#3.#9//java/lang/Object.“:()V
    #9=名称和类型#5:#6/“”:()V
    #10=Utf8行号表
    #11=Utf8 LocalVariableTable
    #12=Utf8此
    #13=Utf8 Ltest/Test1;
    #14=Utf8源文件
    #15=Utf8 Test1.java
    #16=Utf8 someInjectedMethod
    #17=Utf8 java/lang/System
    #18=类#17//java/lang/System
    #19=Utf8输出
    #20=Utf8 Ljava/io/PrintStream;
    #21=名称和类型#19:#20//out:Ljava/io/PrintStream;
    #22=Fieldref#18.#21//java/lang/System.out:Ljava/io/PrintStream;
    #23=Utf8注入示例
    #24=字符串#23//注入示例
    #25=Utf8 java/io/PrintStream
    #26=类#25//java/io/PrintStream
    #27=Utf8打印LN
    #28=Utf8(Ljava/lang/String;)V
    #29=名称和类型#27:#28//println:(Ljava/lang/String;)V
    #30=Methodref#26.#29//java/io/PrintStream.println:(Ljava/lang/String;)V
    #31=Utf8运行时访问说明
    #32=Utf8 Ltest/TestAnnotationToShowitnconstanttable;
    #33=Utf8合成
    {
    public com.qubit.augmentation.test.Test1();
    签字:()五
    旗帜:ACC_PUBLIC
    有限公司