目录

  • SpringBoot 常见处理异常的方式
  • 一.使用 @RestControllerAdvice 和 @ExceptionHandler 处理全局异常
  • 1. 新建异常信息实体类
  • 2. 自定义异常类型
  • 3. 新建异常处理类
  • 4. controller模拟抛出异常
  • 5. 编写测试类
  • 二.使用 @ExceptionHandler 处理 Controller 级别的异常
  • 三.ResponseStatusException
  • ResponseStatusException 提供了三个构造方法


SpringBoot 常见处理异常的方式

这里讲一下spring boot 3种常见的处理异常的方式。

一.使用 @RestControllerAdvice 和 @ExceptionHandler 处理全局异常

Spring 项目必备的全局处理 Controller 层异常。

这是目前很常用的一种方式,非常推荐。测试中用到了 Junit 5,如果你新建项目验证下面的代码的话,记得添加上相关依赖。

1. 新建异常信息实体类

非必要的类,主要用于包装异常信息。

/**
 * @author song
 */
public class ErrorResponse {
    private String message;
    private String errorTypeName;

    public ErrorResponse(Exception e) {
        this(e.getClass().getName(), e.getMessage());
    }

    public ErrorResponse(String errorTypeName, String message) {
        this.errorTypeName = errorTypeName;
        this.message = message;
    }
    ......省略getter/setter方法
}

2. 自定义异常类型

一般我们处理的都是 RuntimeException ,所以如果你需要自定义异常类型的话直接集成这个类就可以了。

/**
 * @author song
 * 自定义异常类型
 */
public class ResourceNotFoundException extends RuntimeException {
    private String message;

    public ResourceNotFoundException() {
        super();
    }

    public ResourceNotFoundException(String message) {
        super(message);
        this.message = message;
    }
    
	public ResourceNotFoundException(Throwable cause){
		super(cause);
	}
	
	public ResourceNotFoundException(String message,Throwable cause){
		super(message,cause);
	}
	
    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

3. 新建异常处理类

相关的3个注解如下:

  • @ControllerAdvice :注解定义全局异常处理类
  • @RestControllerAdvice :注解定义全局异常处理类
  • @ExceptionHandler :注解声明异常处理方法
    ControllerAdvice 和 RestControllerAdvice 区别也就是和 Controller 与 RestController 一样。现在基本都是Spring boot 和rest风格开发用的是@RestControllerAdvice。还可以用assignableTypes指定特定的 Controller 类
/**
 * @author song
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    ErrorResponse illegalArgumentResponse = new ErrorResponse(new IllegalArgumentException("参数错误!"));
    ErrorResponse resourseNotFoundResponse = new ErrorResponse(new ResourceNotFoundException("Sorry, the resourse not found!"));

	// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理一种异常
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(Exception e) {

        if (e instanceof IllegalArgumentException) {
            return ResponseEntity.status(400).body(illegalArgumentResponse);
        } else if (e instanceof ResourceNotFoundException) {
            return ResponseEntity.status(404).body(resourseNotFoundResponse);
        }
        return null;
    }
}

4. controller模拟抛出异常

/**
 * @author song
 */
@RestController
@RequestMapping("/api")
public class ExceptionController {

    @GetMapping("/illegalArgumentException")
    public void throwException() {
        throw new IllegalArgumentException();
    }

    @GetMapping("/resourceNotFoundException")
    public void throwException2() {
        throw new ResourceNotFoundException();
    }
}

使用 Get 请求 localhost:8080/api/resourceNotFoundException(curl -i -s -X GET url),服务端返回的 JSON 数据如下:

{
    "message": "Sorry, the resourse not found!",
    "errorTypeName": "com.saijia.common.exception.ResourceNotFoundException"
}

5. 编写测试类

MockMvc 由org.springframework.boot.test包提供,实现了对Http请求的模拟,一般用于我们测试 controller 层。

/**
 * @author song
 */
@AutoConfigureMockMvc
@SpringBootTest
public class ExceptionTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    void should_return_400_if_param_not_valid() throws Exception {
        mockMvc.perform(get("/api/illegalArgumentException"))
                .andExpect(status().is(400))
                .andExpect(jsonPath("$.message").value("参数错误!"));
    }

    @Test
    void should_return_404_if_resourse_not_found() throws Exception {
        mockMvc.perform(get("/api/resourceNotFoundException"))
                .andExpect(status().is(404))
                .andExpect(jsonPath("$.message").value("Sorry, the resourse not found!"));
    }
}

二.使用 @ExceptionHandler 处理 Controller 级别的异常

上面也说了使用@ControllerAdvice注解 可以通过 assignableTypes指定特定的类,让异常处理类只处理特定类抛出的异常。实际上这种方式现在使用的比较少了,这里还是举一下例子。

/**
 * @author song
 */
@ControllerAdvice(assignableTypes = {ExceptionController.class})
@ResponseBody
public class GlobalExceptionHandler {

    ErrorResponse illegalArgumentResponse = new ErrorResponse(new IllegalArgumentException("参数错误!"));
    ErrorResponse resourseNotFoundResponse = new ErrorResponse(new ResourceNotFoundException("Sorry, the resourse not found!"));

    @ExceptionHandler(value = Exception.class)// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理一种异常
    public ResponseEntity<ErrorResponse> exceptionHandler(Exception e) {

        if (e instanceof IllegalArgumentException) {
            return ResponseEntity.status(400).body(illegalArgumentResponse);
        } else if (e instanceof ResourceNotFoundException) {
            return ResponseEntity.status(404).body(resourseNotFoundResponse);
        }
        return null;
    }
}

controller模拟抛出异常

/**
 * @author song
 */
@RestController
@RequestMapping("/api")
public class ExceptionController {

    @GetMapping("/illegalArgumentException")
    public void throwException() {
        throw new IllegalArgumentException();
    }

    @GetMapping("/resourceNotFoundException")
    public void throwException2() {
        throw new ResourceNotFoundException();
    }
}

三.ResponseStatusException

研究 ResponseStatusException 我们先来看看,通过 ResponseStatus注解简单处理异常的方法(将异常映射为状态码)。

@ResponseStatus(code = HttpStatus.NOT_FOUND)
public class ResourseNotFoundException2 extends RuntimeException {

    public ResourseNotFoundException2() {
    }

    public ResourseNotFoundException2(String message) {
        super(message);
    }
}

Controller 层抛出异常测试

@RestController
@RequestMapping("/api")
public class ResponseStatusExceptionController {
    @GetMapping("/resourceNotFoundException2")
    public void throwException3() {
        throw new ResourseNotFoundException2("Sorry, the resourse not found!");
    }
}

使用 Get 请求 localhost:8080/api/resourceNotFoundException2 ,服务端返回的 JSON 数据如下:

{
    "timestamp": "2020-12-08T07:11:43.744+0000",
    "status": 404,
    "error": "Not Found",
    "message": "Sorry, the resourse not found!",
    "path": "/api/resourceNotFoundException2"
}

通过 ResponseStatus注解简单处理异常的方法的好处是比较简单,但是一般我们不会这样做,通过ResponseStatusException会更加方便,可以避免我们额外的异常类。

@GetMapping("/resourceNotFoundException2")
    public void throwException3() {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Sorry, the resourse not found!", new ResourceNotFoundException());
    }

使用 Get 请求 localhost:8080/api/resourceNotFoundException2 ,服务端返回的 JSON 数据如下,和使用 ResponseStatus 实现的效果一样:

{
    "timestamp": "2020-12-08T07:20:53.017+0000",
    "status": 404,
    "error": "Not Found",
    "message": "Sorry, the resourse not found!",
    "path": "/api/resourceNotFoundException2"
}

ResponseStatusException 提供了三个构造方法

public ResponseStatusException(HttpStatus status) {
        this(status, null, null);
    }

    public ResponseStatusException(HttpStatus status, @Nullable String reason) {
        this(status, reason, null);
    }

    public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause) {
        super(null, cause);
        Assert.notNull(status, "HttpStatus is required");
        this.status = status;
        this.reason = reason;
    }

构造函数中的参数:

  • status :http status
  • reason :response 的消息内容
  • cause :抛出的异常

讲到这里本章对SpringBoot处理异常的知识也就结束了,如果想了解更多知识可以在对应的专栏中看系列文章,谢谢大家的观看,希望能给各位同学带来帮助。如果觉得博主写的还可以的,可以点赞收藏。 😉