了解Javassist中的常量池

时间:2012-09-03 13:31:43

标签: java javassist

我正在使用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在运行时创建新类时,获取常量池的正确实例的一般正确方法是什么?

另外,关于窗帘背后发生的事情,我有点迷失:为什么我们需要一个常量池来创建一个合成属性的实例?或者一般来说,还有其他任何类属性的实例?

感谢您的任何澄清。

1 个答案:

答案 0 :(得分:9)

不知道你是否仍然对答案感兴趣,但至少可能会帮助其他人 找到这个问题。

首先,向每个开始创建/修改字节码的人提出一个小建议 并且需要有关JVM内部如何工作的更深入的信息,JVM's specification documentation起初可能看起来笨重而且可怕,但它是非常宝贵的帮助!

  

对CtClass.getClassFile()。getConstPool()的调用是否是在此示例中获取常量池的正确方法?

是的,确实如此。每个Java类都有一个常量池,因此每次需要访问常量时都是基本的 你可以做ctClass.getClassFile().getConstPool()给定班级的游泳池,虽然你必须记住 以下内容:

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

  2. 有些时候您可能没有CtClass,而是CtMethodCtField不允许您回溯到CtClass实例,在这种情况下,您可以使用ctMethod.getMethodInfo().getConstPool()ctField.getFieldInfo().getConstPool()来检索正确的常量池。

    由于我已经提到了CtMethodCtField,请注意,如果您想为其中任何一个添加属性,则无法通过{{1} }对象,但分别通过ClassFileMethodInfo

  3.   

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

    要回答这个问题,我将开始引用section 4.4 regarding JVM 7 specs(就像我说的,这个文档非常有用):

      

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

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

    FieldInfo

    Javap附带java SDK,它是分析这个级别的类的好工具,每个开关的解释

    • -s:打印内部类型签名
    • -c:打印字节代码
    • -p:打印所有类成员(方法和字段,包括私有成员)
    • -v:详细,将打印大头钉信息和类常量池

    这里是我通过javassist修改的javap -s -c -p -v SomeClassFile.class 类的输出,以便在类和test.Test1

    中都具有合成属性
    injectedMethod

    请注意,类和方法都具有属性 Synthetic:true ,这意味着它们是Synthetic但是,合成符号也必须存在于常量池中(检查#33)。 / p>

    关于使用常量池和类/方法属性的另一个示例是使用运行时保留策略添加到someInjectedMethod的注释。该方法的字节码只能引用常量池#32符号,只有在那里你才能学到这一点 注释来自类型test / TestAnnotationToShowItInConstantTable;

    希望现在事情变得更清楚了。