springboot -- 自定义(可预知和不可预知) Exception 异常的抛出和统一捕获处理

导言:

一般异常处理并返回到客户端是因为客户端需要知道错误信息
如果不是这样的话,处理程序的内部异常一般打印日志去标记或者捕获一下就可以了

打印日志:
private static final Logger LOGGER = LoggerFactory.getLogger(PageService.class);

关于异常的执行:

若添加try catch语句则程序会处理异常,try内异常不再执行,处理后继续向下运行
若没有try catch语句 程序会在异常处跳出来,不再运行下面部分

个人理解是你主动 throw 一个异常出来, 如果不用try cath 捕获的话, 下面的代码是不会执行的;
如果你当场用try cath捕获了, 下面的代码会继续执行。


关于异常统一捕获处理

当你在Service层throw抛出了一个异常, 那么如果没有专门的异常捕获类, 异常就要交给Controller层去处理了,我们不希望Controller层处理异常, 所以要设置一个专门的异常捕获类
优点:
1、我们可以对异常信息进行详细描述, 使得返回客户端的信息有辨识度(不单纯发操作失败和错误代码)
2、捕获大部分异常, 减少try catch 代码冗余问题

大概流程思想:
在这里插入图片描述


关于可预知和不可预知异常

可预知:由程序员在代码有可能出错的地方手动抛出一个异常
不可预知:不可预知异常通常由SpringMVC或者第三方依赖等抛出的异常,程序员一般不可知


1、首先自定义一个异常:

package com.xuecheng.framework.exception;

import com.xuecheng.framework.model.response.ResultCode;

/**
 * @author Huang
 * @version 1.0
 * @date 2020/3/27 10:49
 */
public class CustomException extends RuntimeException {
    private ResultCode resultCode;

    public CustomException(){}

    public CustomException(ResultCode resultCode){
        //异常信息为错误代码+异常信息
        super("错误代码:"+resultCode.code()+"错误信息:"+resultCode.message());
        this.resultCode = resultCode;
    }

    public ResultCode getResultCode(){
        return resultCode;
    }
}

1、注意此处的自定义异常类是继承RuntimeException,而不是Exception。
如果继承Exception, 当你throw new Exception时, 程序编译时会要求你在方法处抛出该异常或者用try catch捕获该异常, 对代码有侵入性,而RuntimeException不会有上述要求

2、一定要写super("…");否则会报空指针异常
3、ResultCode resultCode 是异常的返回消息, 返回给客户端即可看到错误的详情信息

2、对抛出异常的语句进行包装(可选择不包装):

package com.xuecheng.framework.exception;

import com.xuecheng.framework.model.response.ResultCode;

/**
 * @author Huang
 * @version 1.0
 * @date 2020/3/27 11:00
 */
//对抛出异常代码进行包装, 使其使用的更简洁(其实区别不大)
public class ExceptionCast {
    public static void cast(ResultCode resultCode){
        throw new CustomException(resultCode);
    }
}

不包装的抛出异常

throw new CustomException(resultCode)

包装后的抛出异常

ExceptionCast.cast(resultCode);

3、此处抛出一个异常:

if(null!=byPageNameAndSiteIdAndPageWebPath){
   ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);
}

注:CmsCode.CMS_ADDPAGE_EXISTSNAME是一个resultCode的实现枚举类对象

4、创建一个处理异常的类(可预知异常):

package com.xuecheng.framework.exception;

import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.framework.model.response.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * 自定义异常统一捕获器
 * @author Huang
 * @version 1.0
 * @date 2020/3/27 11:04
 */

@ControllerAdvice //控制器增强
public class ExceptionCatch {
    private static final Logger logger = LoggerFactory.getLogger(ExceptionCatch.class);
	
	//捕获可预知自定义CustomException类异常的方法
    @ResponseBody
    @ExceptionHandler(CustomException.class)
    public ResponseResult handlerCustomException(CustomException customException){
        //记录日志
        logger.error("捕获到一个可预知异常:" + customException.getMessage());
        //获取该异常的代码
        ResultCode resultCode = customException.getResultCode();
        //返回结果响应体
        return new ResponseResult(resultCode);
    }
}

不可预知异常


package com.xuecheng.framework.exception;

import com.google.common.collect.ImmutableMap;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.framework.model.response.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * 自定义不可预知异常统一捕获器
 * @author Huang
 * @version 1.0
 * @date 2020/3/27 11:04
 */

@ControllerAdvice //控制器增强
public class ExceptionCatch {
    private static final Logger logger = LoggerFactory.getLogger(ExceptionCatch.class);

    //定义exceptionsMap, 存放异常类型和错误代码信息对应key-value
    //ImmutableMap的特点的一旦创建不可改变,并且线程安全
    private static ImmutableMap<Class<? extends Throwable>, ResultCode> exceptionsMap;
    //定义exceptionsMap的builder对象,通过builder存放数据后再赋值给exceptionsMap,构建exceptionsMap
    protected static ImmutableMap.Builder<Class<? extends Throwable>, ResultCode> builder = ImmutableMap.builder();

    //静态方法体, 每次程序运行后,该类只会执行一次
    //初始化builder中不可预知异常类的对应错误信息
    static {
        builder.put(HttpMessageNotReadableException.class, CommonCode.INVALID_PARAM); //缺少实体参数
    }

    //不可预知异常通常由SpringMVC或者第三方依赖等抛出的异常,程序员一般不可知
    //捕获并设置返回信息
    //捕获不可预知自定义Exception类异常的方法
    @ResponseBody
    @ExceptionHandler(BindException.class)
    public ResponseResult handlerException(Exception exception){
        //记录日志
        logger.error("捕获到一个不可预知异常:" + exception.getMessage());
        if(null==exceptionsMap){
            exceptionsMap = builder.build(); //exceptionsMap构建成功
        }
        ResultCode resultCode = exceptionsMap.get(exception.getClass()); //获取对应异常的对应错误信息
        ResponseResult responseResult;
        //找到了对应的错误信息
        if(null!=resultCode){
            responseResult = new ResponseResult(resultCode);
        }
        else{
            responseResult = new ResponseResult(CommonCode.SERVER_ERROR);
        }
        //返回结果响应体
        return responseResult;
    }
}

注:这里的 ImmutableMap 是一个泛型的Map,因为不可预知的异常有N多种,所以要用?泛型来装载这些异常类型(Throwable是所有Exception的父类)
0、 ImmutableMap的特点的一旦创建不可改变,并且线程安全
1、ImmutableMap ----- exceptionsMap:存放异常类型和错误代码信息对应key-value
2、builder对象:通过builder存放数据后再赋值给exceptionsMap,构建exceptionsMap
3、statIc方法块中的就是初始化builder了,Key是异常的类, Value是错误信息


关于部分注解

@ControllerAdvice (增强控制器)代表了全局处理,可以全局处理数据,全局处理异常等。这里运用于全局异常处理, 表示这个类是全局可以访问的。
@ExceptionHandler(UserNoExit.class)表示该方法只处理该异常类型

@ResponseBody表示以json格式返回消息体到客户端


如果抛出异常不处理的话就会显示错误页面(这个页面是自定义过的的):表示服务端出现错误

在这里插入图片描述


成功捕获异常:

在这里插入图片描述
第一行是日志错误信息
第二行是成功捕获异常信息

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值