AspectJの概要 †
- Javaのメソッドに対して、横断的に処理を追加する技術
- たとえば、すべての public メソッドの開始・終了時にログを出力する処理を追加するなど
- 用語
- Aspect = Java の Class に相当。Advice の集合体
- Advice = Java の Method に相当。Method の開始・終了時に埋め込む処理。
- Point Cut = Advice を埋め込むことができるところ。Method の開始・終了など。
- Join Point = Point Cut のうち、実際に Advice が埋め込まれたところ。Advice 側からは、JoinPoint?オブジェクト を通して Method や Object にアクセスできる。
- 当初 Xerox パロアルト研究所で作られた概念。現在 Eclipse.org に移管されて開発中。
- まずは、Aspect 指向プログラミングの代表例*1ともいうべきログ出力をやってみる
Eclipse への ADJT プラグインのインストール †
- AspectJ ホームページ
- AJDT(AspectJ Development Tools) ホームページ
- AJDT のインストール
- [ヘルプ]-[ソフトウェアの更新]-[検索およびインストール]
- 新規サイトとして、http://download.eclipse.org/tools/ajdt/33/dev/update を登録
- あとは、[次へ]-[次へ]
AspectJプロジェクトの作成 †
[ファイル]-[新規]-[その他]
こんなアプリを作る †
- おみくじアプリ ボタンを押すと、OmikujiModel?.createOracle() を呼び出して、おみくじを引く。6回おみくじを引くとエラーになる
- OmikujiUI (ボタン押下イベント部分のみ)
jButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { try{ System.out.println("UI wrote : START actionPerformed()"); System.out.println("UI wrote : CALL OmikujiModel.createOracle()"); String oracle = OmikujiModel.createOracle(); jLabel.setText(oracle); System.out.println("UI wrote : RET OmikujiModel.createOracle()"); System.out.println("UI wrote : TERM actionPerformed()"); return; }catch(Exception ex){ jLabel.setText("ERROR"); }finally{ System.out.println("UI wrote : FINAL actionPerformed()"); } } });
- OmikujiModel?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
- | | | | - - | | | - | ! ! !
package com.snail.exam.aj.omikuji; public class OmikujiModel { private static int count = 0; private static final String[] ORACLES = new String[]{"excellent lucky","lucky","a bit lucky","misfortune","great misfortune"}; public static String createOracle(){ try{ System.out.println("MODEL wrote : START createOracle()"); System.out.println("MODEL wrote : TERM createOracle()"); return ORACLES[ (count++) ]; }finally{ System.out.println("MODEL wrote : FINAL createOracle()"); } } }
ログ出力のAspectを適用する †
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| - | | | | | | | - | ! | | - | ! | | - | ! | | - | ! | | - | ! | | - | ! | | - | ! | | - | ! | - | | | | ! | ! | |
- Point Cut の指定の仕方
@Pointcut("execution(public * com.snail.exam.aj.omikuji.*.*(..)) && !within(OmikujiLogger)")
- call と execution
- call は、メソッド呼び出し時。Join Point は、呼び出し側
- execution は、メソッド実行時。Join Point は、呼び出し先
- @Around アドバイスを使うときには、使い分けが必要 → AspectJ GUIアプリの国際化
- @Before / @After アドバイスでは余り関係ないかも
- アドバイスについて
アドバイスの発生順序 †
ボタンを押したときに、Method と Advice がどういう順番で動くのか? 実際にこの「おみくじプログラム」を動かしてみた。
- 正常時
UI wrote : START actionPerformed() UI wrote : CALL OmikujiModel.createOracle() beforeCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) beforeExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) MODEL wrote : START createOracle() MODEL wrote : TERM createOracle() MODEL wrote : FINAL createOracle() afterExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) returnExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) afterCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) returnCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) UI wrote : RET OmikujiModel.createOracle() UI wrote : TERM actionPerformed() UI wrote : FINAL actionPerformed()
- 例外発生時
UI wrote : START actionPerformed() UI wrote : CALL OmikujiModel.createOracle() beforeCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) beforeExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) MODEL wrote : START createOracle() MODEL wrote : TERM createOracle() MODEL wrote : FINAL createOracle() afterExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) throwExecute() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) throwCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) afterCall() (public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle()) UI wrote : FINAL actionPerformed()
ログの埋め込みだけでもありがたい †
- 想像してみてください。
- もしもあなたが、全くログを出さず、仕様書もなく、担当者もどこかへ消えたスパゲッティー・プログラムの改修を任されたとしたら・・・
- そんなとき、AspectJ を使って、すべてのメソッドの開始時と終了時にログを埋め込んでやれば、問題箇所がどのクラスのどのメソッドなのかを特定できる。地獄に仏 とは正にこのこと
- かぐや・・・