きり丸アドベントカレンダー2020の18記事目です。
RestControllerAdvise
で共通的に例外をキャッチする方法をこちらの記事で紹介しました。
しかし、RestControllerAdvise
は発生した例外をキャッチするのが役割で、例外を自作例外に翻訳することはこのクラスの役割ではありません。各ロジックでキャッチして翻訳するのもいいですが、DBの接続エラー等のハンドリングするのは明らかに同じハンドリングする必要があるので面倒です。
今回の記事ではSpring AOPを使用して、特定の例外を共通的に翻訳します。
翻訳したうえで例外をキャッチするRestControllerAdvise
を組み合わせると有効です。
ゴール
- 特定のパッケージ配下の特定のExceptionを翻訳する
環境
手順
Spring AOPを依存関係に入れる
Spring AOPを使うには依存関係に含める必要があります。
ファイル名:build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
}
翻訳先のExceptionを作成する
今回はSpringのNotFoundExceptionから、自作のNotFoundExceptionに翻訳したいので、自作のNotFoundExceptionを作成します。
public class NotFoundException extends RuntimeException { NotFoundException(Throwable ex){ super(ex); } }
Spring AOPでExceptionをキャッチして翻訳する
@Aspect
と@Component
をクラスに付与する必要があります。
@AfterThrowing
を使用すると、Exceptionをキャッチできます。valueにはキャッチ対象のパッケージ、throwingにパラメータでExceptionを受け取れます。受け取った後で自作例外に翻訳します。
@Aspect @Component public class ExceptionHandlerRepository { @AfterThrowing( value = "execution(* com.example.demo.repository..*(..))", throwing = "ex") public void dataAccessException(DataAccessException ex) { throw new com.example.demo.exception.DataAccessException(ex); } }
Spring AOPのテストを行う
@Mock
を有効にしたいので、@ExtendWith(MockitoExtenstion.class)
を指定します。
AspectJProxyFactory
を使用して、AOPが有効なコードを作成します。具体的なコードは次を参考にしてください。
SpringのDataAccessExceotionはabstractクラスで直接生成できないので、サブクラスをThrowさせます。Throwさせたサブクラスを自作例外に翻訳できることは、AssertJを使用して例外を確認します。
@ExtendWith(MockitoExtension.class) class ExceptionHandlerRepositoryTests { @Mock RepositoryForTest repository; RepositoryForTest proxy; @BeforeEach void beforeEach() { AspectJProxyFactory factory = new AspectJProxyFactory(repository); factory.addAspect(new ExceptionHandlerRepository()); proxy = factory.getProxy(); } @DisplayName("SpringのDataAccessExceptionを自作のDataAccessExceptionに翻訳する") @Test void test_01() { Mockito.doThrow(new InvalidDataAccessResourceUsageException("test")) .when(repository).execute(); assertThatThrownBy(() -> proxy.execute()) .isInstanceOf(DataAccessException.class); } }
ソースコード
アドベントカレンダー18日目。
GitHubのソースコードでは、自作Exception等々は直接RuntimeExceptionではなく、自作の基底例外のBusinessExceptionを継承しています。 github.com
終わりに
AOPを使うと、デバッグが難しくなるので多用しないほうがいいです。
ですので、私はユースケースとしてはDB系の例外を翻訳する以外では思いつきません。もし、他の有効なケースがある場合は教えていただけると幸いです。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。
参考記事
敬称略
公式リファレンス:Spring-Coreの使い方 docs.spring.io
類似記事
きり丸アドベントカレンダー2020 adventar.org
きり丸のHerokuページ
https://kirimaru-todoapp.herokuapp.com/
18日目のアドベントカレンダーの記事 nainaistar.hatenablog.com