Spring MVC 中的异常处理程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6742324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:35:44  来源:igfitidea点击:

Exception handler in Spring MVC

springspring-mvcinterceptorcustom-exceptions

提问by fastcodejava

I want to create an exception handler which will intercept all controllers in my project. Is that possible to do? Looks like I have to put a handler method in each controller. Thanks for your help. I have a spring controller that sends Json response. So if an exception happens I want to send an error response which can be controlled from one place.

我想创建一个异常处理程序,它将拦截我项目中的所有控制器。那有可能吗?看起来我必须在每个控制器中放置一个处理程序方法。谢谢你的帮助。我有一个发送 Json 响应的弹簧控制器。所以如果发生异常,我想发送一个可以从一个地方控制的错误响应。

回答by Ralph

(I found a way to implement it in Spring 3.1, this is described in the second part of this answer)

(我找到了一种在 Spring 3.1 中实现它的方法,这在这个答案的第二部分中有描述)

See chapter 16.11 Handling exceptionsof Spring Reference

参见章节16.11Spring Reference 的异常处理

There are some more ways than using @ExceptionHandler(see gouki's answer)

除了使用之外,还有更多的方法@ExceptionHandler(参见gouki 的回答

  • You could implement a HandlerExceptionResolver(use the servlet not the portlet package) - that is some kind of global @ExceptionHandler
  • If you do not have a specific logic for the exception, but only specifc view then you could use the SimpleMappingExceptionResolver, which is at least an implementation of the HandlerExceptionResolverwhere you can specify an Exception name pattern and the view (jsp) which is shown when the exception is thrown. For example:

    <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>
    
  • 您可以实现一个HandlerExceptionResolver使用 servlet 而不是 portlet 包)——这是某种全局@ExceptionHandler
  • 如果您没有异常的特定逻辑,而只有特定的视图,那么您可以使用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>
    


In Spring 3.2+one can annotate a class with @ControllerAdvice, all @ExceptionHandlermethods in this class work in a global way.

Spring 3.2+ 中,可以使用 注释一个类@ControllerAdvice,该类中的所有@ExceptionHandler方法都以全局方式工作。



In Spring 3.1there is no @ControllerAdvice. But with a little hack one could have a similar feature.

Spring 3.1中没有@ControllerAdvice. 但是只要稍加修改,就可以拥有类似的功能。

The key is the understanding of the way @ExceptionHandlerworks. In Spring 3.1 there is a class ExceptionHandlerExceptionResolver. This class implements (with help of its superclasses) the interface HandlerExceptionResolverand is responsible invoking the @ExceptionHandlermethods.

关键是对工作方式的理解@ExceptionHandler。在 Spring 3.1 中有一个类ExceptionHandlerExceptionResolver。此类实现(在其超类的帮助下)接口HandlerExceptionResolver并负责调用@ExceptionHandler方法。

The HandlerExceptionResolverinterface has only one Method:

HandlerExceptionResolver接口只有一种方法:

ModelAndView resolveException(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              Exception ex);`.

When the request was handled by a Spring 3.x Controller Method, then this method (represented by org.springframework.web.method.HandlerMethod) is the handlerparameter.

当请求由 Spring 3.x 控制器方法处理时,此方法(由 表示org.springframework.web.method.HandlerMethod)就是handler参数。

The ExceptionHandlerExceptionResolveruses the handler(HandlerMethod) to obtain the Controller class and scan it for methods annotated with @ExceptionHandler. If one of this methods matches the exception (ex) then this methods get invoked in order to handle the exception. (else nullget returned in order to signal that this exception resolver feels no responsible).

ExceptionHandlerExceptionResolver用途handlerHandlerMethod),以获得控制器类和扫描它用于与所注解的方法@ExceptionHandler。如果此方法之一与异常 ( ex)匹配,则调用此方法以处理异常。(否则null返回以表明此异常解析器不负责)。

The first idea would be to implement an own HandlerExceptionResolverthat behaves like ExceptionHandlerExceptionResolver, but instead of search for @ExceptionHandlerin the controller class, it should search for them in one special bean. The drawback would be, that one has to (copy (or subclass ExceptionHandlerExceptionResolver) and must) configure all nice message converters, argument resolvers and return value handlers by hand (the configuration of the real one and only ExceptionHandlerExceptionResolveris done by spring automatically). So I came up with another idea:

第一个想法是实现一个自己的HandlerExceptionResolver行为ExceptionHandlerExceptionResolver,但不是@ExceptionHandler在控制器类中搜索,而是应该在一个特殊的 bean 中搜索它们。缺点是,必须(复制(或子类ExceptionHandlerExceptionResolver)并且必须)手动配置所有好的消息转换器、参数解析器和返回值处理程序(真正的配置仅ExceptionHandlerExceptionResolver由 spring 自动完成)。于是我想到了另一个想法:

Implement a simple HandlerExceptionResolverthat "forwards" the exception to THE (already configured) ExceptionHandlerExceptionResolver, BUT with an modified handlerwhich points to the bean that contains the global Exception handlers (I call them global, because they do the work for all controllers).

实现一个简单的HandlerExceptionResolver将异常“转发”到 THE(已配置)ExceptionHandlerExceptionResolver,但修改后handler指向包含全局异常处理程序的 bean(我称它们为 global,因为它们为所有控制器完成工作)。

And this is the implementation: GlobalMethodHandlerExeptionResolver

这是实现: 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);
        }            
    }
}

The global Handler has to implement this interface (in order to get found and to implement the fakeHanderMethodused for the handler

全局 Handler 必须实现这个接口(为了找到并实现fakeHanderMethod用于handler

public interface GlobalMethodExceptionResolverContainer {
    void fakeHanderMethod();
}

And example for an global 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;
    }
}

BTW: You do not need to register the GlobalMethodHandlerExeptionResolverbecause spring automatically register all beans that implements HandlerExceptionResolverfor exception resolvers. So a simple <bean class="GlobalMethodHandlerExeptionResolver"/>is enough.

顺便说一句:您不需要注册,GlobalMethodHandlerExeptionResolver因为 spring 会自动注册HandlerExceptionResolver为异常解析器实现的所有 bean 。所以一个简单的<bean class="GlobalMethodHandlerExeptionResolver"/>就足够了。

回答by Vahe Harutyunyan

Since Spring 3.2 you can use @ControllerAdviceannotation. You can declare an @ExceptionHandlermethod within an @ControllerAdvice class in which case it handles exceptions from @RequestMapping methods from allcontrollers.

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

}

回答by gouki

An abstract class where you define the exception handlers will do. And then make your controllers inherit it.

您可以在其中定义异常处理程序的抽象类。然后让你的控制器继承它。