springboot项目中如何使用全局异常处理和自定义业务异常

24 篇文章 0 订阅

springboot项目中如何使用全局异常处理和自定义业务异常

一. 统一捕获异常

统一捕获异常,使用AOP的思想,解决在controller中大量try-catch重复代码。

  • @RestControllerAdvice : 贴在类上,@RestControllerAdvice的增强,同时可以在controller执行前后做一些额外逻辑。
  • @ExceptionHandler(异常类.class) :贴在方法上,可捕获指定类型的异常。

二. 使用枚举封装返回的异常信息

package io.coderyeah.ymcc.enums;

/**
 * @author lqs
 * @date 2022/10/19 11:12
 */
// 系统错误异常
public enum YmccSystemError {
    SYSTEM_ERROR("10010", "系统错误,正在加班修理中-_-"),
    SYSTEM_OAUTH_ERROR("10020", "你没有权限访问!未授权!"),
    SYSTEM_PARAM_ERROR("10030", "数据格式错误");


    // 错误码
    private String code;
    // 错误信息
    private String message;

    // 构造方法
    YmccSystemError(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

三. 自定义业务异常

package io.coderyeah.ymcc.exception;

import io.coderyeah.ymcc.enums.YmccSystemError;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author lqs
 * @date 2022/10/19 11:03
 */
// 自定义业务异常
@EqualsAndHashCode(callSuper = true)
@Data
public class BusinessException extends RuntimeException {
    private String message;
    private String code;
    private YmccSystemError systemError;

    public BusinessException(YmccSystemError systemError) {
        this.systemError = systemError;
    }

    public BusinessException(String message, String code) {
        this.message = message;
        this.code = code;
    }

    public BusinessException(String message) {
        this.message = message;
    }
}
全局异常代码:包含三种全局异常处理
package io.coderyeah.ymcc.exception;

import io.coderyeah.ymcc.enums.YmccSystemError;
import io.coderyeah.ymcc.result.JSONResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author lqs
 * @date 2022/10/19 11:06
 */
@RestControllerAdvice // controller全局异常处理
@Slf4j
public class GlobalExceptionHandler {

    // 定义需要捕获的业务异常 主动抛出的异常
    @ExceptionHandler(BusinessException.class)
    public JSONResult businessException(HttpServletResponse response, BusinessException ex) {
        log.warn("业务异常:{}", ex.getSystemError().getMessage() + ":" + ex.getSystemError().getCode());
        log.warn("错误代码:{}", response.getStatus());
        return JSONResult.error(ex.getSystemError());
    }

    // 定义需要捕获的系统异常 未知情况下发生的异常
    @ExceptionHandler(Exception.class)
    public JSONResult globalException(HttpServletResponse response, Exception ex) {
        ex.printStackTrace();
        log.warn("全局系统异常:{}", ex.getMessage());
        log.warn("错误代码:{}", response.getStatus());
        return JSONResult.error(YmccSystemError.SYSTEM_ERROR);
    }

    // 定义需要捕获的参数异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JSONResult parameterException(HttpServletResponse response, MethodArgumentNotValidException ex) {
        ex.printStackTrace();
        final StringBuffer paramInfo = new StringBuffer("数据格式错误:");
        final List<ObjectError> errors = ex.getBindingResult().getAllErrors();
        errors.forEach(err -> {
            paramInfo.append(err.getDefaultMessage()).append(";");
        });
        // 去除末尾分号
        paramInfo.deleteCharAt(paramInfo.lastIndexOf(";"));
        log.warn("参数异常:{}", paramInfo.toString());
        log.warn("错误代码:{}", response.getStatus());
        return JSONResult.error(paramInfo.toString(), YmccSystemError.SYSTEM_PARAM_ERROR.getCode());
    }

}
统一返回结果类
package io.coderyeah.ymcc.result;

import io.coderyeah.ymcc.enums.YmccSystemError;
import io.coderyeah.ymcc.exception.BusinessException;
import lombok.Builder;
import lombok.Data;

//返回JSON结果
@Data
//建造者模式
//@Builder
public class JSONResult {

    private boolean success = true;

    private String message = "成功";

    //错误码,用来描述错误类型 ,1000 表示么有错误
    private String code = "1000";

    //返回的数据
    private Object data;

    /**
     * 创建当前实例
     **/
    public static JSONResult success() {
        return new JSONResult();
    }

    /**
     * 创建当前实例
     **/
    public static JSONResult success(Object obj) {
        JSONResult instance = new JSONResult();
        instance.setData(obj);
        return instance;
    }

    public static JSONResult success(Object obj, String code) {
        JSONResult instance = new JSONResult();
        instance.setCode(code);
        instance.setData(obj);
        return instance;
    }

    /**
     * 创建当前实例
     **/

    public static JSONResult error(String message, String code) {
        JSONResult instance = new JSONResult();
        instance.setMessage(message);
        instance.setSuccess(false);
        instance.setCode(code);
        return instance;
    }

    public static JSONResult error() {
        JSONResult jsonResult = new JSONResult();
        jsonResult.setSuccess(false);
        return jsonResult;
    }

    /**
     * 创建当前实例
     **/
    public static JSONResult error(String message) {
        return error(message, null);
    }

    public static JSONResult error(YmccSystemError ex) {
        return error(ex.getMessage(), ex.getCode());
    }

    public static JSONResult error(BusinessException ex) {
        JSONResult jsonResult = new JSONResult();
        jsonResult.setSuccess(false);
        jsonResult.setMessage(ex.getMessage());
        return jsonResult;
    }
}

四.JSR303校验

  1. 导入依赖
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-validation</artifactId>
       <version>2.5.10</version>
    </dependency>
    
  2. Bean Validation 中内置的 constraint

相关注解

Constraint详细信息
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式
Constraint详细信息
@Email被注释的元素必须是电子邮箱地址
@Length被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串的必须非空
@Range被注释的元素必须在合适的范围内
  1. 在参数实体类的字段上注解
    @NotBlank(message = "姓名不允许为空")
    @TableField("real_name")
    private String realName;
    
@Pattern(regexp = "^1(3\\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$", message = "手机号格式错误")
private String tel;
@Email(message = "邮箱格式错误")
private String email;
  1. 开启校验

    @Valid 或者 @Validated都可以标识该类需要进行校验,在类上也可以加该注解。

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public JSONResult saveOrUpdate(@Valid @RequestBody User user) {
        if (user.getId() != null) {
            userService.updateById(user);
        } else {
            user.insert(user);
        }
        return JSONResult.success();
    }
    

五.自定义校验注解

  1. 定义校验注解
    package io.coderyeah.ymcc.anno;
    
    import io.coderyeah.ymcc.validator.PhoneValidator;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.*;
    
    /**
     * @author lqs
     * @date 2022/10/19 
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(validatedBy = PhoneValidator.class)//此处指定了注解的实现类为PhoneValidator
    public @interface Phone {// 用来校验手机号码的注解
    
        String message() default "无效的手机格式";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    
  2. 定义校验器
    package io.coderyeah.ymcc.validator;
    
    import io.coderyeah.ymcc.anno.Phone;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * @author lqs
     * @date 2022/10/19 
     */
    public class PhoneValidator implements ConstraintValidator<Phone, String> {
    
        private static final String PHONE_REGEX = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[013678])|(18[0-9]))\\d{8}$";
    
        @Override
        public void initialize(Phone constraintAnnotation) {
            //初始化
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
            //对值进行手机号正则判断
            Pattern p = Pattern.compile(PHONE_REGEX);
            Matcher m = p.matcher(value);
            return m.matches();
        }
    }
    
  3. 使用校验注解
    // 校验手机号
    @Phone 
    private String tel;
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值