不知道你是否仍然對答案感興趣,但至少可以幫助其他人找到這個問題 。
首先,一個小小的建議給大家,開始創建/修改字節碼 ,需要在JVM內部構件如何工作,在JVM's specification documentation看起來笨重,並在第一嚇人更深入的信息,但它是無價的幫助!
是否調用CtClass.getClassFile()。getConstPool()在此示例中獲取常量池的正確方法是?
是的。每個Java類都有一個常量池,所以basicaly您需要訪問不變 池給定類,你可以做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(就像我說的,這個文檔是非常有幫助的):
的Java虛擬機指令不依賴於類,接口運行時佈局,類實例或數組。相反,指令引用constant_pool表中的符號信息。
考慮到這一點,我認爲在這個問題上最好的方法是看一下clas文件轉儲。
javap -s -c -p -v SomeClassFile.class
的javap自帶的Java SDK和它的分析在這個級別類的好工具,每個解釋切換
- -s:打印,我們可以通過運行以下命令做到這一點內部類型簽名
- -c:打印字節碼
- -p:打印所有的類成員(方法和字段,包括私營的)
- -v:詳細輸出,將打印粘性信息和類常量池
下面是輸出爲test.Test1
類,我通過了Javassist修飾以具有合成屬性無論是在類和在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
}
注意兩個類,並且該方法具有屬性合成:真正這意味着它們是合成的,但是,你的綜合符號也必須存在於常量池(查看#33)。
關於使用常量池和類/方法的屬性的另一例子是將註釋添加運行時保留策略來someInjectedMethod。該方法的字節碼只需要常量池#32符號的引用,只有在那裏你將瞭解到 的註釋是從類型測試/ TestAnnotationToShowItInConstantTable;
希望事情變得多一點明確了,你知道。