21

プロジェクト内のすべてのコントローラーをインターセプトする例外ハンドラーを作成したいと考えています。それは可能ですか?各コントローラーにハンドラーメソッドを配置する必要があるようです。ご協力いただきありがとうございます。Json 応答を送信するスプリング コントローラーがあります。したがって、例外が発生した場合は、1 か所から制御できるエラー応答を送信したいと考えています。

4

3 に答える 3

26

(Spring 3.1で実装する方法を見つけました。これは、この回答の2番目の部分で説明されています)

16.11 Spring Reference の例外の処理を参照してください。

使用以外にもいくつかの方法があります@ExceptionHandlergouki's answerを参照)

  • HandlerExceptionResolverを実装できます(ポートレット パッケージではなくサーブレットを使用します)。これは、ある種のグローバルな @ExceptionHandler です。
  • 例外に特定のロジックがなく、特定のビューしかない場合は、SimpleMappingExceptionResolverを使用できます。これは、少なくともHandlerExceptionResolverException 名パターンを指定できる の実装であり、そのときに表示されるビュー (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 クラスを取得し、それをスキャンして で注釈が付けられたメソッドを探します。このメソッドのいずれかが例外 ( ) に一致する場合、例外を処理するためにこのメソッドが呼び出されます。(そうでなければ、この例外リゾルバーが責任を感じていないことを知らせるために返されます)。handlerHandlerMethod@ExceptionHandlerexnull

最初のアイデアは、の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"/>で十分です。

于 2011-07-19T07:35:38.333 に答える
15

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
    // ...

}
于 2013-04-18T06:54:14.287 に答える
9

例外ハンドラーを定義する抽象クラスが行います。そして、コントローラーにそれを継承させます。

于 2011-07-19T05:26:36.793 に答える