SpringBoot--全局异常处理--方法/实例

原文网址:SpringBoot--全局异常处理--方法/实例_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文用实例介绍SpringBoot如何进行全局异常处理。

方案简述

         全局异常处理可以用两个注解来实现:@ControllerAdvice+@ExceptionHandler。这样可以捕获 Controller中抛出的指定类型异常,可对不同类型的异常单独进行处理。

系列文章

SpringBoot全局处理我写了一个系列:

  1. SpringBoot--全局异常处理--方法/实例_IT利刃出鞘的博客-CSDN博客
  2. SpringBoot--全局响应处理--方法/实例_IT利刃出鞘的博客-CSDN博客
  3. SpringBoot--全局请求处理--方法/实例_IT利刃出鞘的博客-CSDN博客
  4. SpringBoot--全局格式处理--方法/实例_IT利刃出鞘的博客-CSDN博客

@ControllerAdvice介绍

说明

        @ControllerAdvice里边有@Component,用于类,可包含@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。(因此是全局的处理方法)。

        可以在多个类上使用@ControllerAdvice,可通过@Order来控制顺序。不能通过实现Order接口来控制顺序,因为这部分源码里只支持@Order这种方式。见:调整多个ControllerAdvice的执行顺序_felixu的博客-CSDN博客

        在Spring4及之后, @ControllerAdvice支持配置控制器的子集,可通过annotations(), basePackageClasses(), basePackages()方法选择控制器子集。

扫描二维码关注公众号,回复: 14258266 查看本文章

官网网址

springmvc 注解总结 - SpringMVC中文官网

使用场景

  1. @ControllerAdvice+@ExceptionHandler:全局异常处理
    1. 捕获Controller中抛出的指定类型异常,可对不同类型的异常区别处理
  2. @ControllerAdvice+ 实现ResponseBodyAdvice接口:全局响应处理(返回值)
    1. 其标注的方法将会在目标Controller方法执行之后执行。
  3. @ControllerAdvice+@ModelAttribute:全局请求处理
    1. 其标注的方法将会在目标Controller方法执行之前执行。
    2. 使用场景示例:鉴权/授权、全局处理格式
  4. @ControllerAdvice+@InitBinder:全局请求处理
    1. 其标注的方法将会在目标Controller方法执行之前执行。
    2. request中自定义参数解析方式进行注册
    3. 使用场景示例:全局处理格式
      SpringBoot--LocalDateTime--全局格式转换/前后端/前端入参/响应给前端_IT利刃出鞘的博客-CSDN博客

实例(简单)

代码

返回值用任何类型都可以,类似Controller的返回值。

package com.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;

@Slf4j
@ControllerAdvice
public class GlobalExceptionAdvice {
    @ExceptionHandler({Exception.class})
    @ResponseBody
    public Object handleException(HttpServletRequest request, Exception e) throws Exception {
        log.error(e.getMessage(), e);
        // 如果某个自定义异常有@ResponseStatus注解,就继续抛出
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", "全局异常处理" + e.getMessage());
        return map;
    }
}

controller

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/advice")
    public String test() {
        int i = 1 / 0;
        return "test success";
    }
}

测试

访问:http://localhost:8080/advice

前端结果:

{
    "message": "全局异常处理/ by zero"
}

后端结果:

2021-08-18 00:39:20.240 ERROR 31064 --- [nio-8080-exec-1] c.example.config.GlobalExceptionAdvice   : / by zero

java.lang.ArithmeticException: / by zero
	at com.example.controller.HelloController.test(HelloController.java:11) ~[classes/:na]
	...

实例:项目实战

代码

异常处理类

package com.example.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestControllerAdvice
// 也可以:类上加@ControllerAdvice + 方法上加@ResponseBody
public class GlobalExceptionAdvice {
    // 处理其他所有异常
    @ExceptionHandler(Exception.class)
    public Result<Object> handleException(Exception e) throws Exception {
        log.info("这是异常处理");
        log.error(e.getMessage(), e);
 
        // 如果某个自定义异常有@ResponseStatus注解,就继续抛出
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
 
        // 实际项目中应该这样写,防止用户看到详细的异常信息
        // return new Result().failure().message.message("操作失败");
        return new Result<>().failure().message(e.getMessage());
    }
 
    // 处理自定义异常
    @ExceptionHandler(BusinessException.class)
    public Result<Object> handleBusinessException(Exception e) throws Exception {
        log.error(e.getMessage(), e);
 
        // 如果某个自定义异常有@ResponseStatus注解,就继续抛出
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
 
        // 实际项目中应该这样写,防止用户看到详细的异常信息
        // return new Result<>().failure().message("操作失败");
        return new Result<>().failure().message(e.getMessage());
    }
}

或者这么写

​import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
// 也可以:类上加@ControllerAdvice + 方法上加@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e){
        log.error(e.getMessage(), e);

        // 如果某个自定义异常有@ResponseStatus注解,就继续抛出
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        
        // 处理所有业务异常
        if (e instanceOf BusinessException.class){
            return Result.failure().message(e.getMessage());
        }

        // 防止用户看到详细的异常信息
        return Result.failure().message("操作失败");
    }
}

Controller(无需处理异常了)

package com.example.business.controller;

import com.example.business.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("fault")
    public void fault() {
        int i = 1 / 0;
    }

}

测试

postman访问:http://localhost:8080/user/fault

postman结果

​​

后端结果

​​

公共代码

下边这两个类好几处都用到了,单独把它提出来。

 User实体类

package com.example.business.entity;
 
import lombok.AllArgsConstructor;
import lombok.Data;
 
@Data
@AllArgsConstructor
public class User {
    private Long id;
    private String userName;
    private Integer age;
}

响应的包装

package com.example.demo.common.entity;

import lombok.Data;

@Data
public class Result<T> {
    private boolean success = true;

    private int code = 1000;

    private String message;

    private T data;

    public Result() {
    }

    public Result(boolean success) {
        this.success = success;
    }

    public Result<T> success(boolean success) {
        Result<T> result = new Result<>(success);
        if (success) {
            result.code = 1000;
        } else {
            result.code = 1001;
        }
        return result;
    }

    public Result<T> success() {
        return success(true);
    }

    public Result<T> failure() {
        return success(false);
    }

    /**
     * @param code {@link ResultCode#getCode()}
     */
    public Result<T> code(int code) {
        this.code = code;
        return this;
    }

    public Result<T> message(String message) {
        this.message = message;
        return this;
    }

    public Result<T> data(T data) {
        this.data = data;
        return this;
    }
}

自定义异常

package com.example.common.exception;

public class BusinessException extends RuntimeException{
    public BusinessException() {
        super();
    }

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

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

猜你喜欢

转载自blog.csdn.net/feiying0canglang/article/details/125229759