2012-09-03 28 views
2

我正在使用Javassist在運行時擴展某些類。 在幾個地方(在代碼中),我需要創建Javassist ConstPool類的實例。 例如,標記生成的類爲synthetic,我寫了這樣的事情:瞭解Javassist中的常量池

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創建新類時,如何獲得常量池的正確實例的常規方法是什麼?

此外,我對這裏幕後發生的事情有些遺憾:爲什麼我們需要一個常量池來創建綜合屬性的實例?或者一般來說,還需要其他任何類屬性?

感謝您的任何澄清。

回答

6

不知道你是否仍然對答案感興趣,但至少可以幫助其他人找到這個問題 。

首先,一個小小的建議給大家,開始創建/修改字節碼 ,需要在JVM內部構件如何工作,在JVM's specification documentation看起來笨重,並在第一嚇人更深入的信息,但它是無價的幫助!

是否調用CtClass.getClassFile()。getConstPool()在此示例中獲取常量池的正確方法是?

是的。每個Java類都有一個常量池,所以basicaly您需要訪問不變 池給定類,你可以做ctClass.getClassFile().getConstPool()每一次,但你必須記住 如下:

  1. 在了Javassist的來自CtClass的常量池字段是一個實例字段,這意味着如果您有兩個CtClass對象 表示同一個類,則會有兩個不同的常量池實例(儘管它們代表實際類文件中的常量池 )。修改其中一個CtClass實例時,您必須使用 關聯的常量池實例才能獲得預期的行爲。

  2. 有時候,你可能沒有足夠的CtClass而是CtMethodCtField它不讓你回溯到CtClass情況下,在這種情況下,你可以使用ctMethod.getMethodInfo().getConstPool()ctField.getFieldInfo().getConstPool()檢索正確的常量池。

    既然我已經提到CtMethodCtField,請記住,如果你想將屬性添加到任何這些,它不能是通過ClassFile對象,而是通過分別MethodInfoFieldInfo

爲什麼我們需要一個常量池中創建?,或在一般的合成屬性的情況下,任何其他類型的類屬性的?

要回答這個問題,我會開始引用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;

希望事情變得多一點明確了,你知道。