自定义异常:可预知异常处理 与 不可预知异常处理

概念

自定义异常:可预知异常处理 与 不可预知异常处理_第1张图片
对可预知异常和不可预知异常的处理方案:
1、自定义异常类型。
2、自定义错误代码及错误信息。
3、对于可预知的异常由程序员在代码中主动抛出,由SpringMVC统一捕获。
可预知异常是程序员在代码中手动抛出本系统定义的特定异常类型,由于是程序员抛出的异常,通常异常信息比较
齐全,程序员在抛出时会指定错误代码及错误信息,获取异常信息也比较方便。
4、对于不可预知的异常(运行时异常)由SpringMVC统一捕获Exception类型的异常。
不可预知异常通常是由于系统出现bug、或一些不要抗拒的错误(比如网络中断、服务器宕机等),异常类型为
RuntimeException类型(运行时异常)。
5、可预知的异常及不可预知的运行时异常最终会采用统一的信息格式(错误代码+错误信息)来表示,最终也会随请求响应给客户端。

处理步骤流程:
1、在controller、service、dao中程序员抛出自定义异常;springMVC框架抛出框架异常类型
2、统一由异常捕获类捕获异常,并进行处理
3、捕获到自定义异常则直接取出错误代码及错误信息,响应给用户。
4、捕获到非自定义异常类型首先从Map中找该异常类型是否对应具体的错误代码,如果有则取出错误代码和错误
信息并响应给用户,如果从Map中找不到异常类型所对应的错误代码则统一为99999错误代码并响应给用户。
5、将错误代码及错误信息以Json格式响应给用户。

应用背景

问题代码背景

//添加页面
public CmsPageResult add(CmsPage cmsPage){
	//校验页面是否存在,根据页面名称、站点Id、页面webpath查询
	CmsPage cmsPage1 = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
    
    if(cmsPage1==null){
        cmsPage.setPageId(null);//添加页面主键由spring data 自动生成
        cmsPageRepository.save(cmsPage);
        //返回结果
        CmsPageResult cmsPageResult = new CmsPageResult(CommonCode.SUCCESS,cmsPage);
        return cmsPageResult;
    }
    return new CmsPageResult(CommonCode.FAIL,null);
}

目标需求

1、上边的代码只要操作不成功仅向用户返回“错误代码:11111,失败信息:操作失败”,无法区别具体的错误信
息。
2、service方法在执行过程出现异常在哪捕获?在service中需要都加try/catch,如果在controller也需要添加
try/catch,代码冗余严重且不易维护。

解决方案

1、在Service方法中的编码顺序是先校验判断,有问题则抛出具体的异常信息,最后执行具体的业务操作,返回成
功信息。
2、在统一异常处理类中去捕获异常,无需controller捕获异常,向用户返回统一规范的响应信息。

可预知异常处理

自定义异常:可预知异常处理 与 不可预知异常处理_第2张图片
自定义异常类:

package com.xuecheng.framework.exception;

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

/**
 * 自定义异常类型
 */
public class CustomException extends RuntimeException {

    //异常错误代码
    ResultCode resultCode;

    public CustomException(ResultCode resultCode){
        this.resultCode = resultCode;
    }

    public ResultCode getResultCode(){
        return this.resultCode;
    }
}

异常抛出类:

package com.xuecheng.framework.exception;

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

/**
 * 异常抛出类
 */
public class ExceptionCast {

    public static void cast(ResultCode resultCodeObj){
        throw new CustomException(resultCodeObj);
    }

}

异常捕获类:

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;
import org.springframework.web.bind.annotation.ResponseBody;

/*
统一异常捕获类
 */
@ControllerAdvice//控制器增强,用于捕获异常的控制器注解.
public class ExceptionCatch {

    //定义一个Logger日志记录变量,用于异常(由捕获类所产生的)日志.
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionCatch.class);


    @ExceptionHandler(CustomException.class) // 括号内为异常抛出类,只要遇到CustomException这个类抛出的异常,就去捕获.
    @ResponseBody //用于将异常信息以Json字符串的形式响应到前端.
    public ResponseResult customExceptionOfResponseResult(CustomException customException){
       //记录日志
        LOGGER.error("catch exception:{}",customException.getMessage());

        ResultCode resultCode = customException.getResultCode();
        return new ResponseResult(resultCode);
    }


}

要使用到异常的方法:

   // 新增页面
    public CmsPageResult add(CmsPage cmsPage){
        //1.检查页面是否存在 通过查询页面名称,站点id,页面webpath(虚拟路径)实现效验.
        CmsPage cmsPageResult = cmsPageRepositoryObj.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(),
                cmsPage.getSiteId() ,cmsPage.getPageWebPath() );

            if (cmsPageResult != null){//如果不为空,则说明存在该页面,无法新增

                //抛出异常
                ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);

            }

            //如果为空则说明数据库不存在该页面,则可以添加

        cmsPage.setPageId(null);//对页面主键传递空参数,由spring data自动生成
        cmsPageRepositoryObj.save(cmsPage); //将其形参出接收到的Controller层传来的数据保存到mongoDB数据库

        //返回结果到controller层
        return new CmsPageResult(CommonCode.SUCCESS,cmsPage);

    }

不可预知异常处理

异常捕获类:
注意:不可预知的异常捕获类并没有捕获你自定义的抛出异常类,而是直接捕获最高级别的’Exception.Class’异常.所以无论是需求上还是逻辑上都不需要应用到@自定义异常类型的类,以及@抛出异常的类.
因为只要某个业务层(如PageService这个类)触发(发生)了异常,那么就意味着触发了Exception.Class(因为所有异常都继承于它),进而就会触发本异常捕获类.

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.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/*
统一异常捕获类
 */
@ControllerAdvice//控制器增强,用于捕获异常的控制器注解.
public class ExceptionCatch {

    //定义一个Logger日志记录变量,用于异常(由捕获类所产生的)日志.
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionCatch.class);



    //定义用于容纳异常类型的Map,存放异常类型所对应的错误代码.
    private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;

    //定义上面map的builder对象,去构建上面的ImmutableMap
    protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builderObj = ImmutableMap.builder();
    static {
        //为该对象异常类型(class)和对应的异常代码
        builderObj.put(HttpMessageNotReadableException.class, CommonCode.INVALID_PARAM);
    }


    @ExceptionHandler(Exception.class) // 括号内为异常抛出类,只要遇到CustomException这个类抛出的异常,就去捕获.
    @ResponseBody //用于将异常信息以Json字符串的形式响应到前端.
    public ResponseResult customExceptionOfResponseResult(Exception e){

       //记录日志
        LOGGER.error("catch exception:{}",e.getMessage());


        if (EXCEPTIONS == null){//如果尚未构建用于处理未知异常的ImmutableMap对象,则进行构建
           EXCEPTIONS = builderObj.build();//构建!
        }
        // 从EXCEPTIONS中寻找异常类型对应的错误代码,如果找到了将错误代码响应给用户,如果找不到就返回给用户9999
      final ResultCode resultCode = EXCEPTIONS.get(e.getClass());
        if (resultCode != null){//判断是否为未知异常.肺空(即map中获取不到这种类型的异常类)则为未知异常

            return new ResponseResult(resultCode);//如果为未知异常,则返回CommonCode的 10003,"非法参数!"

        }else {
            return new ResponseResult(resultCode);//如果不为空,则是不为未知异常,而为更大的不可预知的异常,则返回CommonCode的 99999,"抱歉,系统繁忙,请稍后重试!"
                                                    //当然,我们可以在map集合中添加此异常的异常class类,同时添加对应的异常代码和字符串信息.这个要根据具体的业务需求来编写了.

        }




    }


}

你可能感兴趣的:(JAVA)