234390216 阅读(9) 评论(0)

SpringMVC之ControllerAdvice

SpringMVC从3.2版本开始提供了一个org.springframework.web.bind.annotation.ControllerAdvice注解,使用它标注的Class在启用了注解的支持时,在进行Class扫描时能够自动扫描到它,因为它上面使用了org.springframework.stereotype.Component注解标注。ControllerAdvice标注的Class的作用是用来辅助Controller的,我们可以在ControllerAdvice标注的Class中使用@ExceptionHandler@InitBinder@ModelAttribute标记对应的方法,以便它们可以对指定范围内的Controller起作用。@ExceptionHandler是用来进行异常处理的,关于它的介绍可以参考笔者以前写的一篇博文http://elim.iteye.com/blog/1188161。关于@InitBinder的介绍可以参考笔者以前写的http://elim.iteye.com/blog/1190065。关于@ModelAttribute的介绍可以参考笔者写的http://elim.iteye.com/blog/1753271。在没有ControllerAdvice时,我们定义的@ExceptionHandler@InitBinder@ModelAttribute都只能在Controller中对当前Controller生效,如果需要让更多的Controller应用相同的逻辑,只能把它们定义在共同的父类中。有了ControllerAdvice后则可以把它们定义在使用@ControllerAdvice标注的Class中。比如下面的示例中就在ControllerAdvice标注的Class中定义了两个使用ExceptionHandler标注的方法以处理对应的异常,其中handleException()方法将用来处理除java.lang.IllegalStateException以外的所有其它异常,它的处理结果是转到一个固定的视图上。而handleIllegalStateException()将用来处理java.lang.IllegalStateException,它的处理结果是以JSON的形式响应的。

@ControllerAdvice
public class DefaultExceptionHandler {

    /**
     * 该方法将处理SpringMVC处理过程中抛出的所有的异常,
     * 将使用该方法的返回值来替换正常的Controller方法的返回值
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception e) {
        return new ModelAndView("viewName");
    }
    
    /**
     * 该方法将处理SpringMVC过程中抛出的所有的java.lang.IllegalStateException,
     * 而其它异常的处理还由上面定义的handleException()处理。当抛出了一个异常可以同时被
     * 多个@ExceptionHandler标注的方法处理时,对应的异常将交由更精确的异常处理方法处理。
     * 
     * 且抛出该异常时将把处理结果以@ResponseBody的形式返回,此时将被当作JSON返回。
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalStateException.class)
    @ResponseBody
    public Object handleIllegalStateException(IllegalStateException e) {
        Map<String, Object> jsonObj = new HashMap<>();
        jsonObj.put("errorMessage", e.getMessage());
        return jsonObj;
    }
    
}

在@ExceptionHandler标注的处理方法中如果希望获取到当前抛出的异常,则可以在方法参数中声明一个需要处理的异常类型的参数,SpringMVC在调用对应的处理方法处理异常时将传递当前的异常对象。 @ExceptionHandler标注的处理方法可以声明任何正常的处理器方法可以声明的参数类型,比如HttpServletRequest、HttpServletResponse、java.util.Map、Model等。 @ExceptionHandler标注的处理方法的返回结果也可以跟正常的Controller处理方法拥有一样的返回类型,比如String、Model、ModelAndView、void、Object等,所以我们可以把它看作是和Controller处理方法等价的方法定义的这么一个方法。

ControllerAdvice定义的Class是有作用范围的,默认情况下,什么参数都不指定时它的作用范围是所有的范围。ControllerAdvice提供了一些可以缩小它的处理范围的参数。

  • value:数组类型,用来指定可以作用的基包,即将对指定的包下面的Controller及其子包下面的Controller起作用。
  • basePackages:数组类型,等价于value。
  • basePackageClasses:数组类型,此时的基包将以指定的Class所在的包为准。
  • assignableTypes:数组类型,用来指定具体的Controller类型,它可以是一个共同的接口或父类等。
  • annotations:数组类型,用来指定Class上拥有指定的注解的Controller。

1、下面的ControllerAdvice将对定义在com.elim.app.mvc.controller包及其子包中的Controller起作用。

@ControllerAdvice(value="com.elim.app.mvc.controller")
public class DefaultExceptionHandler {

    //...
    
}

2、下面的ControllerAdvice也将对定义在com.elim.app.mvc.controller包及其子包中的Controller起作用。

@ControllerAdvice(basePackages="com.elim.app.mvc.controller")
public class DefaultExceptionHandler {

    //...
    
}

3、下面的ControllerAdvice也将对定义在com.elim.app.mvc.controller包及其子包中的Controller起作用。它通过basePackageClasses指定了需要作为基包的Class,此时基包将以basePackageClasses指定的Class所在的包为准,即com.elim.app.mvc.controller

@ControllerAdvice(basePackageClasses=com.elim.app.mvc.controller.Package.class)
public class DefaultExceptionHandler {

    //...
    
}

4、下面的ControllerAdvice将对FooController及其子类型的Controller起作用。

@ControllerAdvice(assignableTypes=FooController.class)
public class DefaultExceptionHandler {

    //...
    
}

5、下面的ControllerAdvice将对所有Class上使用了RestController注解标注的Controller起作用。

@ControllerAdvice(annotations=RestController.class)
public class DefaultExceptionHandler {

    //...
    
}

6、也可以同时指定多个属性,比如下面的ControllerAdvice将对FooController及其子类型的Controller起作用,同时也将对com.elim.app.mvc.controller包及其子包下面的Controller起作用。

@ControllerAdvice(assignableTypes=FooController.class, basePackages="com.elim.app.mvc.controller")
public class DefaultExceptionHandler {

    //...
    
}

当一个异常可以同时被多个ControllerAdvice的@ExceptionHandler标注的方法处理时只有第一个匹配的处理器方法可以处理。

(本文是基于Spring4.1.0所写)