複数の@ ControllerAdvice @ ExceptionHandlersの優先順位を設定する


83

で注釈が付けられた複数のクラスがあり@ControllerAdvice、それぞれにの@ExceptionHandlerメソッドがあります。

Exception特定のハンドラーが見つからない場合は、これを使用することを意図して処理します。

悲しいことに、Spring MVCExceptionは、より具体的なケース(IOExceptionたとえば)ではなく、常に最も一般的なケース()を使用しているようです。

これは、Spring MVCの動作をどのように期待するのでしょうか?私はJerseyのパターンをエミュレートしようとしていExceptionMapperます。これは、各(同等のコンポーネント)を評価して、処理する宣言された型がスローされた例外からどれだけ離れているかを判断し、常に最も近い祖先を使用します。

回答:


127

これは、Spring MVCの動作をどのように期待するのでしょうか?

Spring 4.3.7以降、SpringMVCの動作は次のとおりHandlerExceptionResolverです。インスタンスを使用してハンドラーメソッドによってスローされた例外を処理します。

デフォルトでは、Web MVC構成は単一のHandlerExceptionResolverBean、aを登録しますHandlerExceptionResolverComposite

他のリストに委任しHandlerExceptionResolversます。

それらの他のリゾルバは

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

その順番で登録しました。この質問の目的のために、私たちは気にするだけExceptionHandlerExceptionResolverです。

AbstractHandlerMethodExceptionResolver例外を解決している@ExceptionHandler方法。

コンテキストの初期化時に、Springは検出したアノテーション付きクラスControllerAdviceBeanごとにを生成@ControllerAdviceします。ExceptionHandlerExceptionResolver文脈からこれらを取得し、それらを使用して使用してソートされますAnnotationAwareOrderComparatorどの

は、OrderComparatorSpringのOrdered インターフェース@Order@Priorityアノテーションをサポートする拡張機能であり、静的に定義されたアノテーション値(存在する場合)をオーバーライドするOrderedインスタンスによって提供されるオーダー値を使用します。

次にExceptionHandlerMethodResolver、これらのControllerAdviceBeanインスタンスごとにを登録します(使用可能な@ExceptionHandlerメソッドを、処理する予定の例外タイプにマッピングします)。これらは最終的に同じ順序でLinkedHashMap(反復順序を保持する)に追加されます。

例外が発生すると、ExceptionHandlerExceptionResolverはこれらを繰り返しExceptionHandlerMethodResolver処理し、例外を処理できる最初の例外を使用します。

だからここでのポイントは以下のとおりです。あなたが持っている場合@ControllerAdvice@ExceptionHandlerするためにException、別の前に登録されることを@ControllerAdvice持つクラス@ExceptionHandlerより具体的な例外、等のためのIOException最初のものが呼び出されますことを、。前述したように、あなたの持っていることによって、その登録順序を制御することができ@ControllerAdvice、注釈付きクラスが実装しOrderedたりして、それに注釈を付ける@Order@Priority、それに適切な値を与えます。


5
さらに、内に複数の@ExceptionHandlerメソッドがある場合@ControllerAdvice、スローされた例外の最も具体的なスーパークラスを処理するメソッドが選択されます。
VijayAggarwal19年

Spring Boot 2.3.3では、親コントローラーアドバイスクラスのコントローラーアドバイスExceptionHandlerメソッドをオーバーライドするサブクラスに@Orderアノテーションを付ける必要はありません
VadirajPurohit20年

93

Sotirios Delimanolisは彼の回答に非常に役立ちました。さらに調査したところ、春の3.2.4で、とにかく@ControllerAdviceアノテーションを検索するコードは、@ Orderアノテーションの存在もチェックし、ControllerAdviceBeansのリストを並べ替えることがわかりました。

@Orderアノテーションのないすべてのコントローラーのデフォルトの順序はOrdered#LOWEST_PRECEDENCEです。つまり、最低の優先順位が必要なコントローラーが1つある場合は、すべてのコントローラーの順序を高くする必要があります。

これは、UserProfileExceptionまたはRuntimeExceptionのいずれかが発生したときに適切な応答を提供できるControllerAdviceアノテーションとOrderアノテーションを持つ2つの例外ハンドラークラスを作成する方法を示す例です。

class UserProfileException extends RuntimeException {
}

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
    @ExceptionHandler(UserProfileException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleUserProfileException() {
        ....
    }
}

@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {

    @ExceptionHandler(RuntimeException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleRuntimeException() {
        ....
    }
}
  • ControllerAdviceBean#initOrderFromBeanType()を参照してください
  • ControllerAdviceBean#findAnnotatedBeans()を参照してください
  • ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()を参照してください

楽しい!


21

例外ハンドラの順序は、@Orderアノテーションを使用して変更できます。

例えば:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomExceptionHandler {

    //...

}

@Orderの値は任意の整数にすることができます。


5

私はまた、ドキュメントで次のことを発見しました:

https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-org.springframework。 web.method.HandlerMethod-java.lang.Exception-

ExceptionHandlerMethod

保護されたServletInvocableHandlerMethodgetExceptionHandlerMethod(HandlerMethod handlerMethod、Exception exception)

指定された例外の@ExceptionHandlerメソッドを見つけます。デフォルトの実装では、最初にコントローラーのクラス階層内のメソッドを検索し、見つからない場合は、@ ControllerAdvice Spring管理のBeanが検出されたと想定して、追加の@ExceptionHandlerメソッドの検索を続行します。パラメータ:handlerMethod-例外が発生したメソッド(nullの場合があります)exception-発生した例外戻り値:例外を処理するメソッド、またはnull

つまり、この問題を解決したい場合は、それらの例外をスローするコントローラー内に特定の例外ハンドラーを追加する必要があります。グローバルデフォルト例外ハンドラーを処理する唯一のControllerAdviceを定義するためのANd。

これによりプロセスが簡素化され、問題を処理するためにOrderアノテーションは必要ありません。


2

「優秀でconvered似たような状況があります春のMVCでの例外処理と題したセクションでの春のブログの記事」グローバル例外処理。彼らのシナリオには、例外クラスに登録されているResponseStatusアノテーションをチェックし、存在する場合は、フレームワークにそれらを処理させるために例外を再スローすることが含まれます。この一般的な戦術を使用できる可能性があります-そこにもっと適切なハンドラーがあり、再スローする可能性があるかどうかを判断してみてください。

あるいは、代わりに見ることができる他のいくつかの例外処理戦略がカバーされています。


1

扱うべき重要なクラス:

**@Order(Ordered.HIGHEST_PRECEDENCE)**
public class FunctionalResponseEntityExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(FunctionalResponseEntityExceptionHandler.class);

    @ExceptionHandler(EntityNotFoundException.class)
    public final ResponseEntity<Object> handleFunctionalExceptions(EntityNotFoundException ex, WebRequest request)
    {
        logger.error(ex.getMessage() + " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.getMessage(),
                request.getDescription(false),HttpStatus.NOT_FOUND.toString());
        return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
    }
}

優先度の低いその他の例外

@ControllerAdvice
    public class GlobalResponseEntityExceptionHandler extends ResponseEntityExceptionHandler
    {
    private final Logger logger = LoggerFactory.getLogger(GlobalResponseEntityExceptionHandler.class);
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request)
    {
        logger.error(ex.getMessage()+ " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.toString(),
                request.getDescription(false),HttpStatus.INTERNAL_SERVER_ERROR.toString());
    }
    }

0

以下のように数値を使用することもできます

@Order(value = 100)

値が小さいほど優先度が高くなります。デフォルト値は* {@ code Ordered.LOWEST_PRECEDENCE}で、最低の優先度を示します(他の*指定された注文値に負けます)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.