プロジェクト内のすべてのコントローラーをインターセプトする例外ハンドラーを作成したいと考えています。それは可能ですか?各コントローラーにハンドラーメソッドを配置する必要があるようです。ご協力いただきありがとうございます。Json 応答を送信するスプリング コントローラーがあります。したがって、例外が発生した場合は、1 か所から制御できるエラー応答を送信したいと考えています。
3 に答える
(Spring 3.1で実装する方法を見つけました。これは、この回答の2番目の部分で説明されています)
章16.11 Spring Reference の例外の処理を参照してください。
使用以外にもいくつかの方法があります@ExceptionHandler
(gouki's answerを参照)
- HandlerExceptionResolverを実装できます(ポートレット パッケージではなくサーブレットを使用します)。これは、ある種のグローバルな @ExceptionHandler です。
例外に特定のロジックがなく、特定のビューしかない場合は、SimpleMappingExceptionResolverを使用できます。これは、少なくとも
HandlerExceptionResolver
Exception 名パターンを指定できる の実装であり、そのときに表示されるビュー (jsp) です。例外がスローされます。例えば:<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">dataAccessFailure</prop> <prop key=".TypeMismatchException">resourceNotFound</prop> <prop key=".AccessDeniedException">accessDenied</prop> </props> </property> </bean>
Spring 3.2+では、クラスに でアノテーションを付けることができます。このクラス@ControllerAdvice
のすべてのメソッドはグローバルな方法で機能します。@ExceptionHandler
Spring 3.1にはありません@ControllerAdvice
。しかし、少しハックすれば、同様の機能を持つことができます。
重要なのは、仕組みを理解することです@ExceptionHandler
。Spring 3.1 には class がありExceptionHandlerExceptionResolver
ます。このクラスは (そのスーパークラスの助けを借りて) インターフェイスを実装しHandlerExceptionResolver
、メソッドの呼び出しを担当し@ExceptionHandler
ます。
HandlerExceptionResolver
インターフェイスにはメソッドが 1 つだけあります。
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);`.
リクエストが Spring 3.x コントローラー メソッドによって処理された場合、このメソッド ( で表されるorg.springframework.web.method.HandlerMethod
) がhandler
パラメーターです。
は( ) をExceptionHandlerExceptionResolver
使用して Controller クラスを取得し、それをスキャンして で注釈が付けられたメソッドを探します。このメソッドのいずれかが例外 ( ) に一致する場合、例外を処理するためにこのメソッドが呼び出されます。(そうでなければ、この例外リゾルバーが責任を感じていないことを知らせるために返されます)。handler
HandlerMethod
@ExceptionHandler
ex
null
最初のアイデアは、のHandlerExceptionResolver
ように動作する own を実装することですが、コントローラー クラスExceptionHandlerExceptionResolver
で検索するのではなく@ExceptionHandler
、1 つの特別な Bean でそれらを検索する必要があります。欠点は、ExceptionHandlerExceptionResolver
すべての素敵なメッセージコンバーター、引数リゾルバー、および戻り値ハンドラーを手動で構成 (コピー (またはサブクラス化) しなければならない) する必要があることです (実際の構成は、Spring によってExceptionHandlerExceptionResolver
自動的にのみ行われます)。そこで私は別のアイデアを思いつきました:
HandlerExceptionResolver
例外を THE (構成済み) に「転送」する単純なものを実装しますが、グローバル例外ハンドラーを含む Bean を指すExceptionHandlerExceptionResolver
変更handler
を加えます (それらはすべてのコントローラーに対して機能するため、グローバルと呼びます)。
そして、これは実装です:GlobalMethodHandlerExeptionResolver
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
public class GlobalMethodHandlerExeptionResolver
implements HandlerExceptionResolver, Ordered {
@Override
public int getOrder() {
return -1; //
}
private ExceptionHandlerExceptionResolver realExceptionResolver;
private List<GlobalMethodExceptionResolverContainer> containers;
@Autowired
public GlobalMethodHandlerExeptionResolver(
ExceptionHandlerExceptionResolver realExceptionResolver,
List<GlobalMethodExceptionResolverContainer> containers) {
this.realExceptionResolver = realExceptionResolver;
this.containers = containers;
}
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
for (GlobalMethodExceptionResolverContainer container : this.containers) {
ModelAndView result = this.realExceptionResolver.resolveException(
request,
response,
handlerMethodPointingGlobalExceptionContainerBean(container),
ex);
if (result != null)
return result;
}
// we feel not responsible
return null;
}
protected HandlerMethod handlerMethodPointingGlobalExceptionContainerBean(
GlobalMethodExceptionResolverContainer container) {
try {
return new HandlerMethod(container,
GlobalMethodExceptionResolverContainer.class.
getMethod("fakeHanderMethod"));
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
グローバル Handler は、このインターフェースを実装する必要があります (見つけfakeHanderMethod
て、handler
public interface GlobalMethodExceptionResolverContainer {
void fakeHanderMethod();
}
グローバル Handler の例:
@Component
public class JsonGlobalExceptionResolver
implements GlobalMethodExceptionResolverContainer {
@Override
public void fakeHanderMethod() {
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorDto handleMethodArgumentNotValidException(
MethodArgumentNotValidException validationException,
Locale locale) {
...
/* map validationException.getBindingResult().getFieldErrors()
* to ValidationErrorDto (custom class) */
return validationErrorDto;
}
}
ところで:Springは例外リゾルバーGlobalMethodHandlerExeptionResolver
を実装するすべてのBeanを自動的に登録するため、登録する必要はありません。HandlerExceptionResolver
だからシンプル<bean class="GlobalMethodHandlerExeptionResolver"/>
で十分です。
Spring 3.2 以降、@ControllerAdviceアノテーションを使用できます。@ControllerAdvice クラス内で@ExceptionHandlerメソッドを宣言できます。この場合、すべてのコントローラーの @RequestMapping メソッドからの例外を処理します。
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(value=IOException.class)
public @ResponseBody String iOExceptionHandler(Exception ex){
//
//
}
// other exception handler methods
// ...
}
例外ハンドラーを定義する抽象クラスが行います。そして、コントローラーにそれを継承させます。