SpringBoot业务开发 03、Springboot异常处理

简介: SpringBoot业务开发 03、Springboot异常处理

一、Springboot处理异常的三种处理方式


1、在Controller上标注@ExceptionHandler异常。


2、全级别异常处理,通过实现HandlerExceptionResolver 接口,接着重写方法,在该方法中处理所有的异常!


3、全局级别异常处理器:@ControllerAdvice+@ExceptionHandler,推荐使用这种方式!!!



二、介绍第三种方法(@ControllerAdvice+@ExceptionHandler)


2.1、全局捕捉异常处理的相关注解


现在对于异常处理普通方法使用@ControllerAdvice+@ExceptionHandler。


介绍几个注解的含义:


@ControllerAdvice:捕捉所有标注@Controller类抛出的异常。


@ExceptionHandler:标记了使用 errorHandlerOverJson() 方法来处理 GlobalErrorInfoException 异常。通常用于指定拦截指定的异常。


@RestControllerAdvice :该注解其实是由@ControllerAdvice与@ResponseBody组成的,我们一般后台捕捉到异常时都会向前端返回json字符串,所有可以直接使用该注解来标注于类,而不用在每个方法上加@ResponseBody。



2.2、响应码设计



ErrorInfoInterface:错误码接口规范,得到code响应码,message响应描述。

CityErrorInfoEnum与GlobalErrorInfoEnum:这两个都是枚举类,都继承了错误码接口规范,我们可以创建多个枚举类来表示不同的情况描述,这两个类一个表示城市错误信息,另一个表示去全局错误信息枚举类!!!

BaseErrorInfoInterface.java:错误码接口规范


/**
 * @author changlu
 * @date 2021/07/22 17:03
 **/
public interface BaseErrorInfoInterface {
    /**
     * 得到错误码
     * @date 2021/07/22 17:04
     * @return java.lang.String
     */
    String getResultCode();
    /**
     * 得到错误信息
     * @date 2021/07/22 17:05
     * @return java.lang.String
     */
    String getResultMsg();
}



commonInfoEnum.java:普通错误枚举类


public enum CommonEnum implements BaseErrorInfoInterface{
    SUCCESS("200", "成功"),
    BODY_NOT_MATCH("400", "请求的数据格式不符"),
    SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"),
    OT_FOUND("404", "未找到该资源!"),
    INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
    SERVER_BUSY("503", "服务器正忙,请稍后再试!"),
    NULLPOINTER_ERROR("1001","空指针异常");
    //错误码
    private String resultCode;
    //描述信息
    private String resultMsg;
    CommonEnum(String resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }
    @Override
    public String getResultCode() {
        return resultCode;
    }
    @Override
    public String getResultMsg() {
        return resultMsg;
    }
}



之后我们若是出现了某种异常情况,可直接拿到对应的枚举实例,也就直接拿到了code与message。



2.3、结果响应类(最终返回给前端)


该类有三个值组成:code、message、data,分别是响应码、描述信息以及data数据(是一个对象)。


最终通过转成JSON字符串形式返回给前端!


ResultBody.java


/**
 * @author changlu
 * @date 2021/07/22 17:15
 **/
@Data
public class ResultBody {
    /**
     * 响应码
     */
    private String code;
    /**
     * 响应消息
     */
    private String message;
    /**
     * 响应结果
     */
    private Object result;
    public ResultBody() {
    }
    /**
     * 响应码与响应结果封装
     */
    public ResultBody(BaseErrorInfoInterface baseErrorInfoInterface) {
        this.code = baseErrorInfoInterface.getResultCode();
        this.message = baseErrorInfoInterface.getResultMsg();
    }
    /**
     * 成功
     * @param data 数据
     * @return xyz.changlu.util.ResultBody
     */
    public static ResultBody success(Object data){
        ResultBody resultBody = new ResultBody(CommonEnum.SUCCESS);
        resultBody.setResult(data);
        return resultBody;
    }
    /**
     * 错误
     * @param baseErrorInfoInterface 枚举类
     * @return xyz.changlu.util.ResultBody
     */
    public static ResultBody error(BaseErrorInfoInterface baseErrorInfoInterface){
        ResultBody resultBody = new ResultBody(baseErrorInfoInterface);
        resultBody.setResult(null);
        return resultBody;
    }
    /**
     * 错误
     * @param code 状态码
     * @param message 描述信息
     * @return xyz.changlu.util.ResultBody
     */
    public static ResultBody error(String code,String message){
        ResultBody resultBody = new ResultBody();
        resultBody.setCode(code);
        resultBody.setMessage(message);
        resultBody.setResult(null);
        return resultBody;
    }
}


2.4、核心:自定义异常类与全局异常捕捉类


自定义异常类:通常自定义异常类继承运行时异常RuntimeException,因为这一类异常是不会强制程序员写try-catch进行捕获的,所以我们可以定义一些来进行手动抛出,之后供全局异常进行捕捉,可设置多个。


全局异常捕捉类:该类用于捕捉@controller注解标注的类抛出的异常,包含自定义异常以及其他异常,我们都可以进行单独编写,一般就这两种。


自定义异常类


public class MsgException extends RuntimeException{
    /**
     * 错误代码
     */
    private String errorCode;
    /**
     * 错误信息
     */
    private String errorMsg;
    public MsgException() {
    }
    public MsgException(String errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
    public MsgException(String message,String errorCode, String errorMsg) {
        super(message);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
    //传入错误信息接口(如枚举类)
    public MsgException(BaseErrorInfoInterface baseErrorInfoInterface,Throwable cause){
        super(baseErrorInfoInterface.getResultCode(), cause);
        this.errorCode = baseErrorInfoInterface.getResultCode();
        this.errorMsg = baseErrorInfoInterface.getResultMsg();
    }
    public MsgException(Throwable cause, String errorCode, String errorMsg){
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
    //set/get方法
    public String getErrorCode() {
        return errorCode;
    }
    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }
    public String getErrorMsg() {
        return errorMsg;
    }
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
    @Override
    public synchronized Throwable fillInStackTrace() {
        return super.fillInStackTrace();
    }
}




全局异常捕捉类


当出现异常后,我们依旧会向前端进行响应返回数据!


/**
 * @author Administrator
 * @date 2021/07/22 17:27
 **/
@RestControllerAdvice
public class MyExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);
    /**
     * 自定义的业务异常(自己向外抛出的异常)
     * @param HttpServletRequest servlet请求
     * @param ex 自定义抛出异常
     * @return xyz.changlu.util.ResultBody
     */
    @ExceptionHandler(value = MsgException.class)
    public ResultBody msgExceptionHandler(HttpServletRequest request,MsgException ex){
        logger.error("自定义的业务异常拦截处理,原因是:" + ex.getErrorMsg());
        return ResultBody.error(ex.getErrorCode(),ex.getErrorMsg());
    }
    /**
     * 空指针异常处理程序
     *
     * @param req 要求的事情
     * @param e   e
     * @return {@link ResultBody}
     */
    @ExceptionHandler(value = NullPointerException.class)
    public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e) {
        logger.error("空指针异常处理!原因是:", e.getMessage());
        return ResultBody.error(CommonEnum.NULLPOINTER_ERROR);
    }
    /**
     * 其他异常处理程序
     *
     * @param req 要求的事情
     * @param e   e
     * @return {@link ResultBody}
     */
    @ExceptionHandler(value = Exception.class)
    public ResultBody exceptionHandler(HttpServletRequest req, Exception e) {
        logger.error("其他异常处理程序!原因是:", e.getMessage());
        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }
}



测试

我们来编写一些Controller来测试全局异常捕捉类是否能够捕捉到指定的异常:


/**
 * @author changlu
 * @date 2021/07/22 17:38
 **/
@RestController
public class UserController {
    //测试自定义异常
    @PostMapping("/user")
    public boolean add(User user){
        System.out.println("开始新增....");
        if(user.getName() == null){
            //抛出自定义异常
            throw new MsgException("-1","用户姓名不能为空!");
        }
        return true;
    }
    //测试空指针异常
    @DeleteMapping("/user/{id}")
    public boolean delete(@PathVariable("id")Integer id){
        System.out.println("开始删除....");
        //抛出空指针
        String str = null;
        str.equals("123");
        return  true;
    }
    //测试其他异常
    @PutMapping("/user/{id}")
    public boolean update(@PathVariable("id")Integer id,User user){
        System.out.println("开始更新....");
        //程序自己出错
        Integer.parseInt("abc123");
        return true;
    }
    @GetMapping("/user/{id}")
    public ResultBody query(@PathVariable("id")Integer id){
        System.out.println("开始查询....");
        return ResultBody.success(null);
    }
}



相关文章
|
7月前
|
缓存 NoSQL Java
springboot业务开发--springboot集成redis解决缓存雪崩穿透问题
该文介绍了缓存使用中可能出现的三个问题及解决方案:缓存穿透、缓存击穿和缓存雪崩。为防止缓存穿透,可校验请求数据并缓存空值;缓存击穿可采用限流、热点数据预加载或加锁策略;缓存雪崩则需避免同一时间大量缓存失效,可设置随机过期时间。文章还提及了Spring Boot中Redis缓存的配置,包括缓存null值、使用前缀和自定义过期时间,并提供了改造代码以实现缓存到期时间的个性化设置。
160 0
|
7月前
|
Java 关系型数据库 MySQL
springboot业务开发--springboot一键生成数据库文档
Screw是一个数据库文档生成工具,能自动化根据数据库表结构生成文档,减轻开发人员工作负担,支持MySQL、MariaDB、TiDB等多种数据库和HTML、Word、Markdown等格式。它依赖HikariCP数据库连接池和Freemarker模板引擎。通过在Spring Boot项目中添加相关依赖并配置,可以用代码或Maven插件方式生成文档。示例代码展示了如何在JUnit测试中使用Screw生成HTML文档。
171 0
|
网络协议 前端开发 Java
SpringBoot+Netty开发IM即时通讯系列(一)
简单来讲,Netty是一个提供了易于使用的API的客户端/服务端框架。Netty并发非常高,一个非阻塞的IO,Netty传输速度也非常快,因为他是0拷贝,什么是零拷贝?NIO中的特性之一就是零拷贝,在Java中,内存分为堆和栈以及字符串常量值等等,如果有一些数据从IO中读取并且放到堆里面,中间会经过一些缓冲区。
1130 0
SpringBoot+Netty开发IM即时通讯系列(一)
|
JSON Java fastjson
使用spring boot开发时java对象和Json对象转换
使用spring boot开发时java对象和Json对象转换
1697 1
使用spring boot开发时java对象和Json对象转换
|
XML NoSQL Java
SpringBoot 开发总结思考(二)
模块装配:假设要注入MongoDB,那么就加上@Configuration注解,有可能一个配置类没办法解决某个方向的问题,往往是很多@Configuration的类组合在一起SpringBoot是使用Enable注解,然后再通过@import导入Selector,通过Selector读取 .factories 文件,最终加载的Configuration
167 0
SpringBoot 开发总结思考(二)
|
XML Java 关系型数据库
SpringBoot 开发总结思考(一)
从面向对象的角度理解XML,是以类和对象作为XML的配置SpringBoot 使用的是配置类加上普通/常规配置的形式,参数不是直接固定在配置类中,而可以写在配置文件中,例如application.properties
142 0
SpringBoot 开发总结思考(一)
|
前端开发 JavaScript API
SpringBoot+Netty开发IM即时通讯系列(二)
通过JS以Ajax异步地让浏览器每隔一段时间(10S)发送请求到后端,去询问服务端是否有新消息、新状态等,如果有则取出并通过前端再渲染。但这很容易造成无限循环,也就是前端Ajax会不停地循环后端的数据
503 0
SpringBoot+Netty开发IM即时通讯系列(二)
|
存储 机器学习/深度学习 IDE
SpringBoot 项目与被开发快速迁移|学习笔记
快速学习 SpringBoot 项目与被开发快速迁移
SpringBoot 项目与被开发快速迁移|学习笔记
|
XML 存储 前端开发
SpringBoot 的 Web 开发|学习笔记
快速学习 SpringBoot 的 Web 开发
116 0
SpringBoot 的 Web 开发|学习笔记
|
Java
springboot进行elasticsearch的开发
springboot进行elasticsearch的开发
208 0
springboot进行elasticsearch的开发
下一篇
DataWorks