对可预知异常和不可预知异常的处理方案:
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捕获异常,向用户返回统一规范的响应信息。
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类,同时添加对应的异常代码和字符串信息.这个要根据具体的业务需求来编写了.
}
}
}