1

javassist を使用して、クラス コンストラクターの次のフィールドを変更しようとしています。

Label Label1 = new Label(new StringBuilder().append(user.name));
Label Label2 = new Label(new StringBuilder().append(user.time.toString());

2 つのラベルの先頭にテキストを追加したい。getText() および setText() を使用して、テキストにアクセスして設定できます。

どうすればこれを達成できますか?

4

1 に答える 1

2

最も簡単なアプローチは、コンストラクター本体をJavaコードで変更し、javassistにバイトコードを作成させる機能を使用することです。

したがって、次のようなことを簡単に行うことができます。

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("package1.package2.ClassToInject");
    /* Notice that in this case I'm going for the default constructor
     * If you want another constructor you just have to materialize the CtClass, for
     * each parameter and pass them in the CtClass Array
     */
CtConstructor declaredConstructor = ctClass.getDeclaredConstructor(new CtClass[] {}); 
 /* Now that you have your constructor you can use insertAfter(), this means, it 
      * will be the last thing to be executed in the constructor. We'll rewrite the 
      * label1 field with our new value. Notice that the string in insertAfter 
      * argument is a regular, valid java code line.
      */
    declaredConstructor.insertAfter("Label1 = new package3.package4.Label(new StringBuilder().append(\"somePrefixMayBeAStringOrAVariableInScope\").append(user.name));");

    // and finally we write the bytecode
ctClass.writeFile("/somePathToPutTheInjectedClassFile/");

また、追加するプレフィックスが他のクラスの静的フィールドである代わりに文字列である場合は、そのクラスの完全修飾名を指定する必要があることにも注意してください。例:.append(package1.package2.SomeClass.SomeField)

これが必要なimportsのは、ソースレベルのみであり、JVMバイトコードを見ると、すべてのクラス参照は完全修飾名に対するものであるためです。

Javassistを使用してこの種の変更を実現する方法の詳細については、javasssistのドキュメントのセクション4.1メソッド本体の最初/最後にソーステキストを挿入するを参照してください。

アップデート

javassistが注入するJavaコードを作成するときは常に、完全修飾クラス名を使用する必要があることに注意してください。そうしないと、javassistのクラスプールが結果のクラスを見つけることができませんjavassist.CannotCompileException

于 2012-12-05T11:29:46.210 に答える