Works by

Ren's blog

アプリケーションバックエンド中心に書いていきます

【Spring】Spring AOP - @AfterThrowing/@Around で例外を再スローする

f:id:rennnosukesann:20181220183020p:plain:w500

Springアプリケーション内で発生する例外をSpring AOPでハンドリングする方法を紹介します。

ある特定の例外を別の例外として再スローするシーンがたまにあるのですが、SpringではこのようなときにAOPによる横断的な例外キャッチを使うと便利です。

例えばDDDを意識したレイヤアーキテクチャをSpringで実装する場合、ドメイン層で投げられる例外をアプリケーション独自の例外にして投げ直したい・・・といった場合に有効です。

Usage

AOPの導入についてはこちらが詳しいです。

www.baeldung.com

導入後、まずは 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 パッケージ配下の、任意の返り値・引数を持つすべてのメソッドを対象としています。

www.baeldung.com

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() は対象メソッドそのものの処理を実行します(引数は不要)。

参考文献

www.baeldung.com www.baeldung.com