本文介绍了Spring MVC中的异常处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

(I在Spring 3.1中找到了一种实现它的方法,对此答案的第二部分对此进行了说明)



请参见 p>

比使用 @ExceptionHandler 还有更多方法(请参阅)




  • 您可以实现,它至少是<$的实现c $ c> HandlerExceptionResolver ,您可以在其中指定异常名称模式和引发异常时显示的视图(jsp)。例如:

     < bean 
    class = org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
    p:defaultErrorView = uncaughtException>
    <属性名称= 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中,有一个类。此类(在其父类的帮助下)实现接口,并负责调用 @ExceptionHandler 方法。



HandlerExceptionResolver 接口只有一个方法:

  ModelAndView resolveException(HttpServletRequest请求,
HttpServletResponse响应,
对象处理程序,
ex异常);`。

当请求由Spring 3.x Controller方法处理时,则此方法(由 org.springframework.web.method.HandlerMethod )是 handler 参数。



ExceptionHandlerExceptionResolver 使用 handler HandlerMethod )来获取Controller类,并对其进行扫描以查找带有 @ExceptionHandler 注释的方法。如果此方法之一与异常( ex )相匹配,则将调用此方法以处理该异常。 (否则返回 null ,以表示该异常解析器不承担任何责任)。



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



实现一个简单的 HandlerExceptionResolver ,将转发给THE(已经已配置) ExceptionHandlerExceptionResolver ,但带有修改后的 handler ,但指向包含全局Exception处理程序的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;


公共类GlobalMethodHandlerExeptionResolver
实现HandlerExceptionResolver,顺序为{

@Override
public int getOrder(){
return- 1; //
}

private ExceptionHandlerExceptionResolver realExceptionResolver;

私人列表< GlobalMethodExceptionResolverContainer>容器;

@Autowired
public GlobalMethodHandlerExeptionResolver(
ExceptionHandlerExceptionResolver realExceptionResolver,
List< GlobalMethodExceptionResolverContainer>容器){
this.realExceptionResolver = realExceptionResolver;
this.containers =容器;
}

@Override
public ModelAndView resolveException(HttpServletRequest请求,
HttpServletResponse响应,
对象处理程序,
异常ex){
用于(GlobalMethodExceptionResolverContainer容器:this.containers){
ModelAndView结果= this.realExceptionResolver.resolveException(
请求,
响应,
handlerMethodPointingGlobalExceptionContainerBean(容器),
例如);
if(结果!= null)
返回结果;
}
//我们认为不负责任
返回null;
}


受保护的HandlerMethod handlerMethodPointingGlobalExceptionContainerBean(
GlobalMethodExceptionResolverContainer container){
try {
return new HandlerMethod(container,
GlobalMethodExceptionResolverContainer .class。
getMethod( fakeHanderMethod));
} catch(NoSuchMethodException | SecurityException e){
抛出新的RuntimeException(e);
}
}
}

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

$ b $的 fakeHanderMethod b

 公共接口GlobalMethodExceptionResolverContainer {
void fakeHanderMethod();
}

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

  @Component 
公共类JsonGlobalExceptionResolver
实现GlobalMethodExceptionResolverContainer {

@Override
public void fakeHanderMethod(){
}


@ExceptionHandler (MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorDto handleMethodArgumentNotValidException(
MethodArgumentNotValidException validateException,
Locale locale){

...
/ *映射validationException.getBindingResult()。getFieldErrors()
*到ValidationErrorDto(自定义类)* /
返回validationErrorDto;
}
}

BTW:您无需注册 GlobalMethodHandlerExeptionResolver 是因为spring会自动注册所有为异常解析器实现 HandlerExceptionResolver 的bean。因此,简单的< bean class = GlobalMethodHandlerExeptionResolver /> 就足够了。


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.

解决方案

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

See chapter 16.11 Handling exceptions of Spring Reference

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

  • 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 HandlerExceptionResolver where 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>
    


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


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

The key is the understanding of the way @ExceptionHandler works. In Spring 3.1 there is a class ExceptionHandlerExceptionResolver. This class implements (with help of its superclasses) the interface HandlerExceptionResolver and is responsible invoking the @ExceptionHandler methods.

The HandlerExceptionResolver interface has only one Method:

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 handler parameter.

The ExceptionHandlerExceptionResolver uses 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 null get returned in order to signal that this exception resolver feels no responsible).

The first idea would be to implement an own HandlerExceptionResolver that behaves like ExceptionHandlerExceptionResolver, but instead of search for @ExceptionHandler in 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 ExceptionHandlerExceptionResolver is done by spring automatically). So I came up with another idea:

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

And this is the implementation: 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 fakeHanderMethod used for the 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 GlobalMethodHandlerExeptionResolver because spring automatically register all beans that implements HandlerExceptionResolver for exception resolvers. So a simple <bean class="GlobalMethodHandlerExeptionResolver"/> is enough.

这篇关于Spring MVC中的异常处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 13:19