SpringMVC中的异常处理程序

SpringMVC中的异常处理程序,spring,spring-mvc,interceptor,custom-exceptions,Spring,Spring Mvc,Interceptor,Custom Exceptions,我想创建一个异常处理程序,它将拦截项目中的所有控制器。这可能吗?看起来我必须在每个控制器中放置一个处理程序方法。谢谢你的帮助。我有一个发送Json响应的spring控制器。因此,如果发生异常,我希望发送一个可以从一个位置控制的错误响应。定义异常处理程序的抽象类就可以了。然后让您的控制器继承它。(我在Spring3.1中找到了一种实现它的方法,这在回答的第二部分中有描述) 参见Spring参考的章节 除了使用@ExceptionHandler,还有更多的方法(请参阅) 您可以实现一个(使用ser

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

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

(我在Spring3.1中找到了一种实现它的方法,这在回答的第二部分中有描述)

参见Spring参考的章节

除了使用
@ExceptionHandler
,还有更多的方法(请参阅)

  • 您可以实现一个(使用servlet而不是portlet包),它是某种全局@ExceptionHandler
  • 如果没有特定的异常逻辑,但只有特定的视图,则可以使用,这至少是
    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>
    
    当请求由Spring3.x控制器方法处理时,此方法(由
    org.springframework.web.Method.HandlerMethod
    表示)是
    handler
    参数

    ExceptionHandlerExceptionResolver
    使用
    handler
    HandlerMethod
    )获取控制器类,并在其中扫描带有
    @ExceptionHandler
    注释的方法。如果此方法之一与异常(
    ex
    )匹配,则会调用此方法以处理异常。(else
    null
    返回,以表示此异常解析程序不承担任何责任)

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

    实现一个简单的
    HandlerExceptionResolver
    ,它将异常“转发”到(已配置的)
    ExceptionHandlerExceptionResolver
    ,但使用一个修改的
    处理程序
    ,该处理程序指向包含全局异常处理程序的bean(我称它们为全局,因为它们对所有控制器都起作用)

    这就是实现:
    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);
            }            
        }
    }
    
    以及全局处理程序的示例:

    @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;
        }
    }
    

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

    因为Spring3.2可以使用注释。 您可以在@ControllerAdvice类中声明一个方法 在这种情况下,它处理来自all控制器的@RequestMapping方法的异常

    @ControllerAdvice
    public class MyGlobalExceptionHandler {
    
        @ExceptionHandler(value=IOException.class)
        public @ResponseBody String iOExceptionHandler(Exception ex){
            //
            //
        }
    
        // other exception handler methods
        // ...
    
    }
    

    你到底想让处理者做什么?@Bozho-问题被更新了。我明白了。我们也遇到了同样的问题,并选择扩展一个定义处理程序的基类(正如gouki所建议的那样),这是一个很好的答案。可以用注释来完成吗?我们有返回Json响应而不是视图的控制器。@fastcodejava:我想没有标准注释。无论如何,你可以自己构建。在最新的Spring参考中,“处理异常”一章是17.11。如果您仍然感兴趣,请参阅@fastcodejava:我找到了一种方法,可以让
    @ExceptionHandler
    方法负责控制器。-请看我的扩展答案。我使用了@ControllerAdvice。它确实会处理异常并返回指定的错误页面,但问题是日志中没有保存任何内容。您能告诉我如何为此启用日志记录吗?在抽象类中定义ExceptionHandler在我的情况下不起作用(Spring 4.1.4)。
    @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;
        }
    }
    
    @ControllerAdvice
    public class MyGlobalExceptionHandler {
    
        @ExceptionHandler(value=IOException.class)
        public @ResponseBody String iOExceptionHandler(Exception ex){
            //
            //
        }
    
        // other exception handler methods
        // ...
    
    }