Spring Boot入門② ～AOP～

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

## AOP（Aspect-Oriented Programming）

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

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

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

このように補助的な処理（関心事）を分離してモジュール化する考え方をAOPと言います。
古いですが、[ここ](http://netail.net/aosdwiki/index.php?%A5%A2%A5%B9%A5%DA%A5%AF%A5%C8%BB%D8%B8%FE%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0)の説明がわかりやすかったです。


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

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

| アノテーション       | タイミング      |
|:-----------------|:------------------|
|@Before	|メソッド実行前に実行される|
|@After	|メソッド実行後に実行される<br>実行結果は問わない|
|@Around	|メソッドの代わりに実行される。<br>メソッド前後の処理|
|@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](https://qiita.com/NagaokaKenichi/items/386af61b6866d60964e8)
[ざっくりとSpringで使うAOPの解釈をする](https://qiita.com/ughirose/items/a7c66782f93cd1ae0d68)
[Springのアスペクト指向プログラミング](https://morizyun.github.io/java/spring-framework-basic-spring-aop.html)
[Spring AOP - ぺーぺーSEのブログ](https://blog.pepese.com/entry/20121210/1355137807)