【Spring】Spring AOP - @AfterThrowing/@Around で例外を再スローする
Springアプリケーション内で発生する例外をSpring AOPでハンドリングする方法を紹介します。
ある特定の例外を別の例外として再スローするシーンがたまにあるのですが、SpringではこのようなときにAOPによる横断的な例外キャッチを使うと便利です。
例えばDDDを意識したレイヤアーキテクチャをSpringで実装する場合、ドメイン層で投げられる例外をアプリケーション独自の例外にして投げ直したい・・・といった場合に有効です。
Usage
AOPの導入についてはこちらが詳しいです。
導入後、まずは Aspect
に相当するクラスを定義します。
@Aspect @Component class ExceptionAspect { }
Aspect
定義後、 @AfterThrowing
アノテーションを付与したメソッドを定義します。
@AfterThrowing
は、引数に取る対象メソッド内で例外送出が発生した場合に呼び出されます。
なおこの例外送出は @AfterThrowing
内で握りつぶし、正常終了とすることはできません(再例外送出による例外上書きは可能)。
@Aspect @Component class ExceptionAspect { @AfterThrowing("execution(* com.rk2.rknikki.domain..*.*(..))", throwing = "e") fun handleException(joinPoint: JoinPoint, e: Throwable): Nothing { when (e) { is AuthenticationException -> throw UnauthorisedException(e.message ?: "", e) else -> throw e } } }
@AfterThrowing
の引数に指定しているのはPointCut式と呼ばれる指定子で、対象メソッドを指定するためのものです。
ここでは、 com.rk2.rknikki.domain
パッケージ配下の、任意の返り値・引数を持つすべてのメソッドを対象としています。
joinPoint
は対象メソッドの情報を保持しており、対象メソッドの引数ラベルや値などの情報を取得できます。
また上記の処理は、 @Around
アノテーションを使用して以下のように書くこともできます。
@Around
アノテーションを使用することで、対象メソッドの前後に処理を挟むことができます。
try-catch等による例外の握りつぶしも可能です。
@Aspect @Component class ExceptionAspect { @Around("execution(* com.rk2.rknikki.domain..*.*(..))") fun handleException(jp: ProceedingJoinPoint): Any? { try { return jp.proceed() } catch (e: Throwable) { when (e) { is AuthenticationException -> throw UnauthorisedException(e.message ?: "") else -> throw e } } } }
jp.proceed()
は対象メソッドそのものの処理を実行します(引数は不要)。