プロジェクト内のすべてのコントローラーをインターセプトする例外ハンドラーを作成したいと思います。それは可能ですか?各コントローラーにハンドラーメソッドを配置する必要があるようです。ご協力いただきありがとうございます。 Json応答を送信するスプリングコントローラーがあります。したがって、例外が発生した場合は、1か所から制御できるエラー応答を送信したいと思います。
(Spring 3.1で実装する方法を見つけました。これは、この回答の2番目の部分で説明されています)
Springリファレンスの 16.11例外の処理 の章を参照してください。
@ExceptionHandler
を使用する以外にもいくつかの方法があります( goukiの回答 を参照)
例外の特定のロジックがなく、特定のビューしかない場合は、 SimpleMappingExceptionResolver を使用できます。これは、少なくともHandlerExceptionResolver
の実装であり、例外を指定できます。名前パターンと、例外がスローされたときに表示されるビュー(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には、クラス ExceptionHandlerExceptionResolver
があります。このクラスは(スーパークラスの助けを借りて)インターフェースを実装します HandlerExceptionResolver
そして@ExceptionHandler
メソッドを呼び出す責任があります。
HandlerExceptionResolver
インターフェースにはメソッドが1つだけあります。
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);`.
リクエストがSpring3.x Controllerメソッドによって処理された場合、このメソッド(org.springframework.web.method.HandlerMethod
で表される)はhandler
パラメーターです。
ExceptionHandlerExceptionResolver
はhandler
(HandlerMethod
)を使用してControllerクラスを取得し、@ExceptionHandler
で注釈が付けられたメソッドをスキャンします。このメソッドの1つが例外(ex
)に一致する場合、このメソッドは例外を処理するために呼び出されます。 (それ以外の場合は、この例外リゾルバーが責任を感じないことを通知するためにnull
が返されます)。
最初のアイデアは、HandlerExceptionResolver
のように動作する独自のExceptionHandlerExceptionResolver
を実装することですが、コントローラークラスで@ExceptionHandler
を検索する代わりに、1つの特別なBeanでそれらを検索する必要があります。欠点は、すべてのNiceメッセージコンバーター、引数リゾルバー、および戻り値ハンドラーを手動で構成する必要がある(コピー(またはサブクラスExceptionHandlerExceptionResolver
)必要がある)ことです(実際の1つだけのExceptionHandlerExceptionResolver
は春までに自動的に行われます)。だから私は別のアイデアを思いついた:
例外をTHE(構成済み)HandlerExceptionResolver
に「転送」する、単純なExceptionHandlerExceptionResolver
を実装しますが、グローバル例外ハンドラーを含むBeanを指す変更された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);
}
}
}
グローバルハンドラーは、このインターフェイスを実装する必要があります(検出され、fakeHanderMethod
に使用されるhandler
を実装するためです。
public interface GlobalMethodExceptionResolverContainer {
void fakeHanderMethod();
}
そして、グローバルハンドラーの例:
@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 メソッドを宣言できます。この場合、allコントローラーからの@RequestMappingメソッドからの例外を処理します。 。
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(value=IOException.class)
public @ResponseBody String iOExceptionHandler(Exception ex){
//
//
}
// other exception handler methods
// ...
}
例外ハンドラーを定義する抽象クラスが行います。そして、コントローラーにそれを継承させます。