技術的な制限により、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()
. つまり、アドバイスの実行の間に内部状態を維持する必要があります。これは可能ですが、もう少し複雑です。これについてさらに議論したい場合は、お知らせください。