@After, @Before, @Finally, @Catch
といったアノテーションを使うと、アクション処理、テンプレート処理の間に、アノテーションを付けたメソッドが実行される。
タイミングは下記のとおり。
@Before
→ action → @After
→ template → @Finally
@Catch
はactionメソッドで例外が発生すると実行される。
影響範囲はコントロールクラス内だが、記述したクラスを継承したり、
@With
でクラス指定されることにより、別のコントロールクラス内の処理でも呼び出される。
疑問
同じコントロールクラス内に同種のインタセプタが複数あった場合、どのような順序で実行されるのだろうか。
以下のようなコードを記述し、実行してみた。
public class Application extends Controller {
@Before
public static void before1() { Logger.info("before1"); }
@Before
public static void before2() { Logger.info("before2"); }
@Before
public static void before3() { Logger.info("before3"); }
public static void index() {
Logger.info("action method.");
render();
}
@After
public static void after1() { Logger.info("after1"); }
@After
public static void after2() { Logger.info("after2"); }
@After
public static void after3() { Logger.info("after3"); }
}
実行結果
14:11:06,590 INFO ~ before2
14:11:06,591 INFO ~ before3
14:11:06,591 INFO ~ before1
14:11:06,591 INFO ~ action
14:11:06,627 INFO ~ after2
14:11:06,627 INFO ~ after1
14:11:06,627 INFO ~ after3
名前順かと思いきや、不定?
どうやって順序を決めているのだろうか。
その前に、アノテーションを付けた処理を実行している箇所を特定。
play.mvc.ActionInvoker.invoke(Request, Response)
どうやらアノテーションの引数に
priority
と言うのがあり、小さい値ほど早く実行されるようだ。
デフォルトが0になっている。すべて同じ値だと、順番はJavaの実装に依存するようだ。
play.utils.JavaWithCaching.findAllAnnotatedMethods(Class<?>)
で取得した結果を、Collections.sort()で並べ替え。比較はpriorityという仕様。
あれ、@Withはどうやって実現しているの?と調べてみると、
findAllAnnotatedMethods
内で実現していた。
アノテーションの探し順。
- 自身クラス内から候補を探す。
- 自クラスに@Withがあれば、そのクラスを探しにいく(そのクラスを基準にfinAllAnnotatedMehtodsを実行)
- 継承元のクラスへ。1番へ戻る。
なので、自クラス内が最優先。自クラスに@With指定があれば、指定されたクラスから探す。
自クラスの継承元クラスへ遡り、再び探す。
継承を遡るほど、後で追加される。
以上はフレームワーク内部の実装であるので、今後も不変とは限らない。なので、この動きを期待すべきではない。
結論
順序を定義したいのなら、
priority
を設定し、優先順を明示すべき。
ちなみに @Util を付けると、actionメソッドの対象から除外される。
public static void
で、action以外のメソッドを作ったときに付与する。