java理解Javassist中的常量池
我正在使用Javassist在运行时扩展某些类。
在一些地方(在生成代码中),我需要创建JavassistConstPool
类的实例。
例如,为了将生成的类标记为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 楼答案
不知道你是否仍然对答案感兴趣,但至少可以帮助其他人 找到这个问题
首先,给每个开始创建/修改字节码的人一个小建议 而且需要更多关于JVM内部如何工作的深入信息,JVM's specification documentation一开始可能看起来笨重可怕,但这是非常宝贵的帮助
是的。每个Java类都有一个常量池,因此每次需要访问该常量时都会使用basicali 对于给定的类,您可以执行
ctClass.getClassFile().getConstPool()
,但必须记住 以下:在javassist中
CtClass
的常量池字段是一个实例字段,这意味着如果您有两个CtClass
对象 表示同一个类时,将有两个不同的常量池实例(即使它们表示 实际类文件中的常量池)。修改其中一个CtClass
实例时,必须使用 关联的常量池实例,以便具有预期的行为有时,您可能没有
CtClass
,而是有一个CtMethod
或一个CtField
,它们不允许您回溯到CtClass
实例,在这种情况下,您可以使用ctMethod.getMethodInfo().getConstPool()
和ctField.getFieldInfo().getConstPool()
来检索正确的常量池既然我已经提到了
CtMethod
和CtField
,请记住,如果要向其中任何一个添加属性,它不能通过ClassFile
对象,而是分别通过MethodInfo
和FieldInfo
对象为了回答这个问题,我将开始引用section 4.4 regarding JVM 7 specs(正如我所说,这个文档非常有用):
考虑到这一点,我认为最好的方法就是查看类文件转储。我们可以通过运行以下命令来实现这一点:
Javap随javasdk一起提供,它是在这个级别分析类的一个很好的工具,解释了每个开关
下面是
test.Test1
类的输出,我通过javassist修改了该类,使其在类和injectedMethod
中都具有合成属性请注意,类和方法都具有属性Synthetic:true,这意味着它们是合成的,但合成符号也必须存在于常量池中(请检查#33)
关于常量池和类/方法属性使用的另一个示例是使用运行时保留策略添加到someInjectedMethod的注释。该方法的字节码只有一个对常量池#32符号的引用,只有在那里您才能了解到这一点 注释来自类型测试/TestAnnotationToShowItInConstantTable
希望你现在能明白一点