Spring MVC中的异常处理程序

fas*_*ava 20 spring spring-mvc custom-exceptions interceptor

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

Ral*_*lph 23

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

请参见第16.11节"处理 Spring Reference的异常 "

还有一些方法比使用@ExceptionHandler(见gouki的答案)

  • 你可以实现一个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>
    
    Run Code Online (Sandbox Code Playgroud)

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


Spring 3.1中没有@ControllerAdvice.但是有点破解可能会有类似的功能.

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

HandlerExceptionResolver接口只有一个方法:

ModelAndView resolveException(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              Exception ex);`.
Run Code Online (Sandbox Code Playgroud)

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

ExceptionHandlerExceptionResolver用途handler(HandlerMethod),以获得控制器类和扫描它用于与所注解的方法@ExceptionHandler.如果其中一个方法与exception(ex)匹配,则调用此方法以处理异常.(否则null返回以表示此异常解析器感觉不负责任).

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

实现一个HandlerExceptionResolver将异常"转发"到THE(已经配置)的简单ExceptionHandlerExceptionResolver,但是带有修改的handler,指向包含全局异常处理程序的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);
        }            
    }
}
Run Code Online (Sandbox Code Playgroud)

全局处理程序必须实现此接口(以便找到并实现fakeHanderMethod用于handler

public interface GlobalMethodExceptionResolverContainer {
    void fakeHanderMethod();
}
Run Code Online (Sandbox Code Playgroud)

以及全局处理程序的示例:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 最新的Spring参考中的"处理异常"一章是17.11.见http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers (2认同)

Vah*_*yan 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
    // ...

}
Run Code Online (Sandbox Code Playgroud)


gou*_*uki 9

您定义异常处理程序的抽象类将执行此操作.然后让你的控制器继承它.