21

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

4

3 回答 3

26

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

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

还有比使用更多的方法@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>
    

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);`.

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

使用ExceptionHandlerExceptionResolver( handler)HandlerMethod获取 Controller 类并扫描它以查找带有 注释的方法@ExceptionHandler。如果其中一种方法与异常 ( ex) 匹配,则调用此方法以处理异常。(否则null返回以表明此异常解析器认为没有责任)。

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

实现一个简单HandlerExceptionResolver的将异常“转发”到 THE (already configured) 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);
        }            
    }
}

全局处理程序必须实现这个接口(为了找到并实现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"/>的就够了。

于 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 回答