あなたがまだ答えに興味があるかどうかはわかりませんが、少なくともこの質問を見つける他の人を助けるかもしれません。
まず第一に、バイトコードの作成/変更を開始し、JVM内部の動作に関するより詳細な情報を必要とするすべての人への小さな提案です。JVMの仕様ドキュメントは、最初はかさばって恐ろしいように見えるかもしれませんが、非常に役立ちます。
この例では、CtClass.getClassFile()。getConstPool()の呼び出しが定数プールを取得する正しい方法ですか?
はい、そうです。各Javaクラスには単一の定数プールがあるため、基本的に、特定のクラスの定数プールにアクセスする必要があるたびに実行できますがctClass.getClassFile().getConstPool()
、次の点に注意する必要があります。
javassistでは、fromの定数プールフィールドCtClass
はインスタンスフィールドです。つまりCtClass
、同じクラスを表す2つのオブジェクトがある場合、(実際のクラスファイルで定数プールを表す場合でも)定数プールの2つの異なるインスタンスがあります。インスタンスの1つを変更する場合CtClass
、期待される動作を実現するには、関連付けられた定数プールインスタンスを使用する必要があります。
インスタンスにバックトレースできないCtClass
aCtMethod
またはaがない場合があります。そのような場合は、正しい定数プールを使用して取得できます。CtField
CtClass
ctMethod.getMethodInfo().getConstPool()
ctField.getFieldInfo().getConstPool()
とについて説明したので、これらのいずれかに属性を追加する場合は、オブジェクトを介してCtMethod
ではなく、それぞれを介して行うことができることに注意してください。CtField
ClassFile
MethodInfo
FieldInfo
合成属性のインスタンス、または一般に他の種類のクラス属性のインスタンスを作成するために定数プールが必要なのはなぜですか?
この質問に答えるために、JVM 7の仕様に関するセクション4.4の引用を開始します(私が言ったように、このドキュメントは非常に役立ちます)。
Java仮想マシンの命令は、クラス、インターフェース、クラスインスタンス、または配列のランタイムレイアウトに依存しません。代わりに、命令はconstant_poolテーブルのシンボリック情報を参照します。
このことを念頭に置いて、この問題に光を当てる最善の方法は、クラスファイルのダンプを調べることだと思います。これは、次のコマンドを実行することで実現できます。
javap -s -c -p -v SomeClassFile.class
JavapにはjavaSDKが付属しており、このレベルでクラスを分析するための優れたツールであり、各スイッチの説明です。
- -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
}
クラスとメソッドの両方に属性Synthetic:trueがあることに注意してください。これは、それらがSyntheticであることを意味しますが、合成シンボルも定数プールに存在する必要があります(チェック#33)。
定数プールおよびクラス/メソッド属性の使用に関する別の例は、実行時保持ポリシーを使用してsomeInjectedMethodに追加されたアノテーションです。メソッドのバイトコードには定数プール#32シンボルへの参照のみがあり、そこでのみ、アノテーションがタイプtest/TestAnnotationToShowItInConstantTableからのものであることがわかります。
物事がもう少し明確になったことを願っています。