7

これは私がやっていることです:

@Aspect
public class MethodLogger {
  @Around("(execution(* *(..)) || initialization(*.new(..))) && @annotation(Foo)")
  public Object wrap(ProceedingJoinPoint point) throws Throwable {
    // works fine, but only for methods
  }
}

スニペットは問題なく動作しますが、メソッド呼び出しに対してのみです。これは、アスペクトを適用した後にAspectJ maven プラグインが言っていることです (コンパイル中ではなく、正常に動作します)。

around on initialization not supported (compiler limitation)

回避策はありますか?私はOpenJDK 7を使用しています:

java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b06)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)
4

2 に答える 2

3

技術的な制限により、around()アドバイスinitialization()preinitialization()ポイントカットなどはありません。また、対応するジョインポイントに出入りするときの時系列順序にも別の問題があります。この例を見てください:

public abstract class ApplicationBase {
    private int id = 0;

    public ApplicationBase(int id) {
        this.id = id;
    }
}
public class Application extends ApplicationBase {
    private String name = "<unnamed>";

    public Application(int id, String name) {
        super(id);
        this.name = name;
    }

    public static void main(String[] args) {
        new Application(1, "Foo");
        new Application(2, "Bar");
    }
}
public aspect ExecutionTimingAspect {
    private String indentText = "";

    pointcut constructorCall() :
        call(*Application*.new(..));

    pointcut constructorRelated() :
        constructorCall() ||
        initialization(*Application*.new(..)) ||
        preinitialization(*Application*.new(..)) ||
        execution(*Application*.new(..));

    after() : constructorRelated() {
        indentText = indentText.substring(2);
        System.out.println(indentText + "<< " + thisJoinPointStaticPart);
    }

    before() : constructorRelated() {
        System.out.println(indentText + ">> " + thisJoinPointStaticPart);
        indentText += "  ";
    }

    Object around() : constructorCall() {
        long startTime = System.nanoTime();
        Object result = proceed();
        System.out.println(indentText + "Constructor runtime = " + (System.nanoTime() - startTime) / 1.0e9 + " s\n");
        return result;
    }
}

次の出力が表示されます。

>> call(Application(int, String))
  >> preinitialization(Application(int, String))
  << preinitialization(Application(int, String))
  >> preinitialization(ApplicationBase(int))
  << preinitialization(ApplicationBase(int))
  >> initialization(ApplicationBase(int))
    >> execution(ApplicationBase(int))
    << execution(ApplicationBase(int))
  << initialization(ApplicationBase(int))
  >> initialization(Application(int, String))
    >> execution(Application(int, String))
    << execution(Application(int, String))
  << initialization(Application(int, String))
<< call(Application(int, String))
Constructor runtime = 0.00123172 s

>> call(Application(int, String))
  >> preinitialization(Application(int, String))
  << preinitialization(Application(int, String))
  >> preinitialization(ApplicationBase(int))
  << preinitialization(ApplicationBase(int))
  >> initialization(ApplicationBase(int))
    >> execution(ApplicationBase(int))
    << execution(ApplicationBase(int))
  << initialization(ApplicationBase(int))
  >> initialization(Application(int, String))
    >> execution(Application(int, String))
    << execution(Application(int, String))
  << initialization(Application(int, String))
<< call(Application(int, String))
Constructor runtime = 0.00103393 s

基本クラスの事前初期化の前に、派生クラスの事前初期化がどのように開始および終了するかがわかりますか? また、初期化はどのように逆に機能するのでしょうか?追加の複雑なコンストラクターの実行が初期化に組み込まれているのでしょうか?

around()おそらく、初期化を測定するだけでは、 を介して可能であったとしても、コンストラクターの全体的な実行時間が反映されないことがわかりました。したがって、呼び出しコードにアクセスできるためではcall()なく、幸運にもコンストラクターをインターセプトできる場合は、問題なく、私の例で行ったように使用することもできます (ちなみに、これはスレッドセーフではありませんが、シンプルにまとめてみました。)呼び出し元に影響を与えることはできず、呼び出し先のみを織り込むことができる場合は、特定のコンストラクターの事前初期化が経由で入力されたときと、同じコンストラクター呼び出しの初期化が経由で終了したときのアスペクト内部の簿記など、他のトリックを使用する必要がありますexecution()around()before()after(). つまり、アドバイスの実行の間に内部状態を維持する必要があります。これは可能ですが、もう少し複雑です。これについてさらに議論したい場合は、お知らせください。

于 2013-03-22T13:19:44.253 に答える
0

回避策として、そのメソッドに関するアドバイスを適用する必要があるため、ファクトリを導入できます。

于 2013-02-18T12:24:34.150 に答える