LoginSignup
6
10

More than 5 years have passed since last update.

Spring Boot入門② ~AOP~

Last updated at Posted at 2018-11-29

前回のDIに続いてAOPについて学習します。
超入門的な内容になりますので、ご了承ください。

AOP(Aspect-Oriented Programming)

アスペクト志向プログラミングは、オブジェクト指向のような開発手法の概念の1つです。
メソッドを書いていると、どうしてもロギング処理や認証処理など、やりたい事と直接関係のない処理が混じりこんでしまいます。
そのような補助的な処理(関心事)を分離して記述しようという開発手法です。
例えば、以下のようなdrinkAlcohol()メソッドがあるとします。

➀開始ログ:酒飲むよー!
    ↓
➁酒を飲む
    ↓
➂終了ログ:酔っぱらったよー!

この場合、①と③は「酒を飲む」行為とは直接関係ない処理なので、別の場所に分離して記述します。
そしてメソッド実行前に①を、実行後に③を実行するように設定します。
これでdrinkAlcohol()メソッドはロギング処理を気にせず、②「酒を飲む」処理だけに集中すればよくなります。

このように補助的な処理(関心事)を分離してモジュール化する考え方をAOPと言います。
古いですが、ここの説明がわかりやすかったです。

用語説明

用語 意味
Aspect 共通処理(関心事)の振る舞いと、適用するポイントをまとめたもの
= AdviceとPointcutをまとめたもの
Advice 分離した共通処理の振る舞い
実際に実行される処理そのもの
JoinPoint Adviceを入れるタイミング
メソッドの実行前、実行後など
PointCut Adviceを適用する条件
JoinPointに達成した時、Adviceを実行するかどうか判定する条件式

JoinPointアノテーション

アドバイスの実行タイミングを設定するアノテーションが用意されています。

アノテーション タイミング
@Before メソッド実行前に実行される
@After メソッド実行後に実行される
実行結果は問わない
@Around メソッドの代わりに実行される。
メソッド前後の処理
@AfterReturning メソッドが正常終了した場合に実行される
@AfterThrowing メソッドで例外が発生した場合に実行される

PointCut指示子

アドバイスを実行する条件式を記載します。
一般的によく使われるexecutionのフォーマットを見てみます。

execution(メソッド修飾子 戻り値 パッケージ名.クラス名.メソッド名(引数の型) throws 例外)

メソッド修飾子、例外のスローは省略可能です。

@Before("execution(* com.sample..*(..))")

上記の例では、com.sampleパッケージ配下にあるメソッドの実行前に実行されます。
条件式にはワイルドカードが利用可能です。

ワイルドカード 意味
* 任意の型、またはクラス名やパッケージ名の一部の代わり
.. 任意の引数、またはクラス名やパッケージ名の一部の代わり
+ クラス名、インターフェース名の右側に書くことで、サブクラスやインターフェースの実装を全て指定

execution以外にも次のような指示子が用意されています。

PointCut指示子 実行条件
within(クラス名) 指定したクラスのメソッドに適用
target(クラス名) 指定したクラスを継承したクラスのメソッドに適用(親クラス、子クラス両方に適用)
args(引数の型) 指定した引数と一致する引数を持つメソッドに適用
@annotation(アノテーション) 指定したアノテーションが付いているメソッドに適用

サンプルコード

上記のdrinkAlcohol()メソッドをAOPで実現してみました。

Drink.java
public class Drink {

    public void drinkAlcohol() {
        System.out.println("drinking...");
    }
}

ロギング処理は書かずに、②酒を飲む処理だけ記述します。

SampleAspect.java
@Aspect
@Component
public class SampleAspect {

    @Before("execution(* com.sample..*(..))")
    public void beforeDrinking() {
        System.out.println("[@Before]start drinking alcohol!");
    }

    @After("execution(* com.sample..*(..))")
    public void afterDrinking() {
        System.out.println("[@After]I got drunk...zzZ");
    }
}

クラスに@Aspect@Componentアノテーションを付与します。
@Beforeが①開始ログ、@Afterが③終了ログになります。

実行結果は以下になります。

[@Before]start drinking alcohol!
drinking...
[@After]I got drunk...zzZ

ちゃんとメソッド前後にログが出力されていますね。

@Around

では、同じ処理を@Aroundで実現してみます。

SampleAspect.java
@Aspect
@Component
public class SampleAspect {

    @Around("execution(* com.sample..*(..))")
    public void aroundDrinking(ProceedingJoinPoint pjp) {
        System.out.println("[@Around]start drinking alcohol!"); // 全処理(➀開始ログ)
        try {
            pjp.proceed(); // メソッドを呼び出す(➁drinkAlcohol())
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("[@Around]I got drunk...zzZ"); // 後処理(➂終了ログ)
    }
}

@Aroundは対象メソッドの代わりにAroundアドバイスが実行されます。
ProceedingJoinPoint.proceed()でメソッドを呼び出すことができるので、その前後にロギング処理を入れる感じですね。

参考資料

SpringでAOP
ざっくりとSpringで使うAOPの解釈をする
Springのアスペクト指向プログラミング
Spring AOP - ぺーぺーSEのブログ

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10