##はじめに
Java Spring FrameworkのAOPについてのお話です。
##@Aspectの利用
たとえば、あるServiceクラスを利用したいクラスが100クラスあって、都度@Autowired
を付与してDIしても実現できますが、あまりスマートではありませんね。
そこで各所で行いたい処理(@Aspect
アノテーションを付与したクラス)を定義し、それを利用したいクラスのメソッドなりにアノテーションを付与することで処理を差し込むことでソースコードの見通しを良くしましょう、というのが趣旨です。
##用語の確認
-
Aspect
横断的な関心事を示すモジュールそのもの。 -
Join Point
横断的な関心事を実行するポイント(メソッド実行時や例外throw時)のこと。 -
Advice
特定のJoinPointで実行されるコードで、横断的な関心事の実装部分。
@Around
が汎用性が高いですが、対象メソッドの処理成功時のみ行いたい処理などタイミングを限定したい場合は目的にあったAdviceを利用したほうがいいかと思います。
@Before 対象のメソッドの処理前に処理される
@AfterReturning 対象のメソッドの処理が成功した際に処理される
@AfterThrowing 対象のメソッドの処理で例外が投げられた際にに処理される
@After 対象のメソッドの処理の成否を問わずに処理が完了した際に処理される
@Around 対象のメソッドの処理の前後に実行される
4.pointcut
実行対象のJoinPointを選択する式。条件を指定することで実行タイミングを詳細に絞り込めます。今回は触れません。下記が参考になります。
https://qiita.com/rubytomato@github/items/de1019aeaaab51c8784d
##具体例
####Aspectを利用するコントローラ
@Controller
@RequestMapping("/hogehoge")
public class MyController {
@GetMapping
@MyAnnotation(hoge = "test", piyo = false)
public String doGet(HttpServletRequest req, HttpServletResponse res) {
// 〜処理たち〜
return "hogehoge";
}
}
####Annotationの定義
このあたりが参考になりました。
https://itsakura.com/java-annotation-make
引数にはプリミティブ型・String・Class・列挙型・アノテーション、あとはそれらの一次元配列のみ指定可能。
https://www.ne.jp/asahi/hishidama/home/tech/java/annotation.html
// Annotationの有効範囲。
@Retention(RetentionPolicy.RUNTIME)
// Annotationを付与したい対象。
@Target(ElementType.METHOD)
public @interface MyAnnotation {
/** 文字列 */
String hoge();
/** boolean */
boolean piyo();
}
####Annotationの定義を引数で受け取るAspect
@Aspect
@Component
public class MyAspect {
@AfterReturning("@annotation(myAnnotation)")
public void after(JoinPoint jp, MyAnnotation myAnnotation) throws Throwable {
// 呼び出し元の引数を受け取ってみる
// ここでは呼び出し元のHttpServletRequest req、HttpServletResponse resが取得できる
Object[] o = jp.getArgs();
// Annotationの引数を受け取ることができる
String hoge = myAnnotation.hoge();
System.out.println(hoge); // test
boolean piyo = myAnnotation.piyo();
System.out.println(piyo); // false
}
}
##まとめ
- 呼び出し元メソッドの引数を受け取ることができるが、引数の「値」なので変数名をキーに処理できない
######呼び出し元メソッドの引数を取得したい場合
protected HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest()
.getParameter("hoge");
}
- Annotationの引数で、変数を渡すことはできない(引数
にはプリミティブ型・String・Class・列挙型・アノテーション、あとはそれらの一次元配列のみ指定可能。) - Annotationの引数を利用する場合としない場合で微妙に書き方が違う
- 利用する場合 前述のサンプルコード通り
- 利用しない場合
@AfterReturning("@annotation(jp.ne.example.MyAnnotation)")
(アノテーションクラスのフルパスを記述)