SpringMvc的Controller中异常处理

Spring中默认的异常处理

在Spring中,有一些异常会默认映射为HTTP状态码,不须要程序处理。下表列出Spring的默认处理异常:java

Spring异常 HTTP状态码
BindException 400 - Bad Request
ConversionNotSupportedException 500 - Internal Server Error
HttpMediaTypeNotAcceptableException 406 - Not Acceptable
HttpMediaTypeNotSupportedException 415 - Unsupported Media Type
HttpMessageNotReadableException 400 - Bad Request
HttpMessageNotWritableException 500 - Internal Server Error
HttpRequestMethodNotSupportedException 405 - Method Not Allowed
MethodArgumentNotValidException 400 - Bad Request
MissingServletRequestParameterException 400 - Bad Request
MissingServletRequestPartException 400 - Bad Request
NoSuchRequestHandlingMethodException 404 - Not Found
TypeMismatchException 400 - Bad Request

上表中的异常会由Spring自身抛出,若是DispatcherServlet处理过程当中或执行校验时出现问题时则直接返回。例如,若是DispatcherServlet没法找到适合处理请求的控制器方法,那么将会抛出NoSuchRequestHandlingMethodException异常,最终的结果就是产生404状态码的响应(Not Found)。web

使用@ResponseStatus处理自定义异常

但对于应用程序内部抛出的自定义异常,它就不能处理了。好比service中抛出了一个自定义异常(NullOrgException),咱们对NullOrgException异常添加@ResponseStatus注解,对其指定状态码便可。spring

以下面的代码:mvc

package com.rebecca.springmvc.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/** * 自定义异常 * @Author: Rebecca * @Description: * @Date: Created in 2019/6/14 17:04 * @Modified By: */

@ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "No Content")
public class NullOrgException extends RuntimeException {
}

复制代码

使用try {…} catch 手动捕获异常

定义了上述异常后,只要应用程序中有抛出NullOrgException异常,就会被捕获并映射为对应的状态码。那么若是程序不单单须要状态码,还要包含所产生的错误,那该怎么办呢?此时的话,咱们就不能将异常视为HTTP错误了,而是要按照处理请求的方式来处理异常了。app

package com.rebecca.springmvc.controller.exception;

import com.rebecca.springmvc.org.bean.Org;
import com.rebecca.springmvc.org.service.OrgService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("test/exception")
public class ExceptionTestController {
    private Logger logger = LoggerFactory.getLogger(ExceptionTestController.class);

    @Autowired
    private OrgService service;

    @RequestMapping(value = "orgs", method = RequestMethod.GET)
    @ResponseBody
    public List<Org> getOrgs () {
        List<Org> orgs = null;
        try {
            orgs = service.getOrgs();
        } catch (NullOrgException e) {
            logger.error("无组织机构相关数据!",e);
        }
        return orgs;
    }
}
复制代码

好比上面代码中的NullOrgException异常,在Spring中没有默认映射,那最简单的办法就是try {…} catch (NullOrgException e) {…}ide

使用@ExceptionHandler处理自定义异常

上述try {…} catch方式不利于代码维护,好在Spring提供了一种机制,能够用@ExceptionHandler注解将异常映射为HTTP状态码。下面是使用@ExceptionHandler注解后的方式:spa

package com.rebecca.springmvc.controller.exception;

import com.rebecca.springmvc.org.bean.Org;
import com.rebecca.springmvc.org.service.OrgService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("test/exception")
public class ExceptionTestController {
    private Logger logger = LoggerFactory.getLogger(ExceptionTestController.class);

    @Autowired
    private OrgService service;

    @RequestMapping(value = "orgs", method = RequestMethod.GET)
    @ResponseBody
    public List<Org> getOrgs () {
        List<Org> orgs = service.getOrgs();
        return orgs;
    }

    @ExceptionHandler(NullOrgException.class)
    public String handleNullOrgException() {
        return "无组织机构相关数据!";
    }
}
复制代码

上面代码咱们在handleNullOrgException()方法上添加了@ExceptionHandler注解,当程序抛出NullOrgException异常时,将会委托该方法来处理。它的返回值是String,你也能够改成其它的返回类型以知足应用程序须要。对于用@ExceptionHandler注解标注的方法来讲,它能处理同一个控制器(Controller)中全部处理器方法所抛出的异常。code

为了不在多个控制器中编写重复的@ExceptionHandler注解方法,咱们会建立一个基础的控制器类,全部控制器类要扩展这个类,从而 继承 通用的@ExceptionHandler方法。继承

使用@ControllerAdvice注解处理全部的异常

前面咱们说使用@ExceptionHandler注解只能处理一个控制器(Controller)中全部处理器方法所抛出的异常,那么有没有一种方法不用集成就可以处理全部控制器中处理器方法所抛出的异常呢?ip

答案是:有!从Spring 3.2开始,这确定是可以实现的,咱们只需将其定义到控制器通知类中便可。

在Spring 3.2以后,为这类问题引入了一个新的解决方案:控制器通知。

控制器通知(controller advice)是指任意带有@ControllerAdvice注解的类。

这个类会包含一个或多个以下类型的方法:

  1. @ExceptionHandler注解标注的方法;
  2. @InitBinder注解标注的方法;
  3. @ModelAttribute注解标注的方法。

在带有@ControllerAdvice注解的类中,上述的这些方法会运用到整个应用程序全部控制器中带有@RequestMapping注解的方法上。@ControllerAdvice注解自己已经使用了@Component,所以@ControllerAdvice注解所标注的类将会自动被组件扫描获取到,和有@Component注解的类同样。@ControllerAdvice最为实用的一个场景就是将全部的@ExceptionHandler方法收集到一个类中,这样全部控制器的异常就能在一个地方进行统一处理。例如,咱们想将NullOrgException的处理方法用到整个应用程序的全部控制器上。以下的程序清单展示的AppWideExceptionHandler就能完成这一任务,这是一个带有@ControllerAdvice注解的类。下面代码使用@ControllerAdvice,为全部的控制器处理异常:

package com.rebecca.springmvc.controller.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/** * 控制器通知类 * @Author: Rebecca * @Description: * @Date: Created in 2019/6/14 16:30 * @Modified By: */
@ControllerAdvice  // 定义控制器类
public class AppWideException {

    // 定义异常处理方法
    @ExceptionHandler(NullOrgException.class)
    public String handleNullOrgException() {
        return "无组织机构相关数据!";
    }
}
复制代码

如今,若是任意的控制器方法抛出了DuplicateSpittleException,无论这个方法位于哪一个控制器中,都会调用这个duplicateSpittleHandler()方法来处理异常。咱们能够像编写@RequestMapping注解的方法那样来编写@ExceptionHandler注解的方法。如程序清单7.10所示,它返回“error/duplicate”做为逻辑视图名,所以将会为用户展示一个友好的出错页面。

总结

Spring中的异常处理:

  1. 特定的Spring异常将会自动映射为指定的HTTP状态码;
  2. 异常 上能够添加@ResponseStatus注解,从而将其映射为某一个HTTP状态码;
  3. 方法 上能够添加@ExceptionHandler注解,使其用来处理异常。
  4. @ExceptionHandler注解的异常方法提取到@ControllerAdvice注解的类中,整个应用程序生效(该方式只适用于Spring3.2+)。

处理异常的最简单方式就是将其映射到HTTP状态码上,进而放到响应之中。

参考

《Spring实战第4版》

相关文章
相关标签/搜索