三、异常处理篇

1、自定义业务异常

概述:该类是为了让在业务层,能够返回错误信息,而不涉及控制层的返回封装类,让业务层和控制层能有效隔离开来。

BusinessException类

@Data
public class BusinessException extends RuntimeException {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "结果码 200:成功 500:具体异常信息 400:参数异常 401:未授权的请求 403:无权访问")private Integer statusCode =500;@ApiModelProperty(value = "错误码")private int errCode;@ApiModelProperty(value = "错误提示信息")private String msg;@ApiModelProperty(value = "数据体")private T data;public BusinessException() {super();}public BusinessException(String message) {super();this.msg = message;}public BusinessException(Integer status, int code, String message) {super();this.statusCode = status;this.errCode = code;this.msg = message;}
}

在业务层Service中,下列错误反馈的例子

//例子一
try {int a =id/0;
} catch (Exception e) {throw new BusinessException(ReturnCode.server_exception_code,ReturnCode.server_exception_message,ReturnCode.err_other_code,"计算出错");
}
//例子二
if (id==null){throw new BusinessException(ReturnCode.param_false_code,ReturnCode.param_false_messageReturnCode.err_notexit_code,"参数不存在");
}

异常抛出之后,捕获异常的方式有两种:

方式一:由控制层进行局部捕获,返回结果

try {calculateService.count(id);return PacketHttpRes_V32.makeOKResponse();
} catch (BusinessException be){log.error("出错:", be.getResultCode_Msg());return PacketHttpRes_V32.makeResponse(be.getReturnCode(), be.getReturnCode_Msg(),be.getResultCode(), be.getResultCode_Msg(),ex.getData());
}

方式二:进行全局捕获,返回结果

@ExceptionHandler(BusinessException.class)
@ResponseBody
public PacketHttpRes_V32 processBusinessException(BusinessException ex) {log.warn("捕获 BusinessException:  ReturnCode:{} , ReturnCodeMsg:{} ResultCode:{} , ResultMsg:{}",ex.getReturnCode(),ex.getReturnCode_Msg(),ex.getResultCode(),ex.getResultCode_Msg());return PacketHttpRes_V32.makeResponse(ex.getReturnCode(), ex.getReturnCode_Msg(),ex.getResultCode(), ex.getResultCode_Msg(),ex.getData());
}

2、校验异常

概述:该异常由于接口参数传入错误,使得异常抛出

常规校验:

注解名称 作用描述
@Null 限制只能为null
@NotNull 限制不能为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于value的数字(小数)
@DecimalMin(value) 限制必须为一个不小于value的数字(小数)
@Max(value) 限制必须是一个不大于value的数字(整数)
@Min(value) 限制必须是一个不小于value的数字(整数)
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不超过fraction
@Future 限制必须是一个将来的日期
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0,集合大小不为0)
@NotBlank 验证注解的元素值不为空,仅应用于字符串且比较时会去除字符串空格
@Email 验证注解的元素值是Email

自定义校验:

IntegerValidator注解:

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IntegerValidatorOfParams.class)
public @interface IntegerValidator {int[] params() default {};String message() default "参数不合法";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

IntegerValidatorOfParams实现类

public class IntegerValidatorOfParams implements ConstraintValidator<IntegerValidator, Object> {private int[] params;@Overridepublic void initialize(IntegerValidator integerValidator) { this.params=integerValidator.params();}@Overridepublic boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {List<Integer> list = Arrays.stream(params).boxed().collect(Collectors.toList());return list.contains(o);}
}

使用案例:

@RestController
@RequestMapping("/TeachingPlan/ShareTpMgr")
@Validated
@Slf4j
@Api(tags = "教师端端共享教案库管理")
public class ShareTpMgrOfTeaController extends BaseController {@ApiOperation(value = "获取共享教材信息列表(中小学)", notes = "获取可访问的教材信息列表以及历史访问信息")@RequestMapping(value = "/GetCanVisBookList", method = RequestMethod.GET)public PacketHttpRes_V32<?> GetCanVisBookList(@ApiParam("用户ID") @NotBlank(message = "--UserID不能为空--") String UserID,@ApiParam("是否查询自己共享的,0或不传查询全部,1为查询自己共享的,2为查询非自己共享的")@RequestParam(value = "IsQueryBySelf", required = false, defaultValue = "0")@NotNull(message = "--IsQueryBySelf不能为空--")@IntegerValidator(params = {0, 1, 2}, message = "--IsQueryBySelf参数错误--") Integer IsQueryBySelf,HttpServletResponse response)
}

全局捕获,返回结果

@ExceptionHandler(value = Exception.class)
public PacketHttpRes<Object> exception(Exception e){String eMessage = e.toString();log.error("全局异常捕获:{}", e);PacketHttpRes<Object> packetHttpRes = new PacketHttpRes<>();if(e instanceof ValidationException || e instanceof ConstraintViolationException|| e instanceof HttpMessageNotReadableException|| e instanceof NumberFormatException|| e instanceof MethodArgumentNotValidException) {packetHttpRes.setStatusCode(ReturnCode.param_false_code);packetHttpRes.setErrCode(ReturnCode.err_nomean_code);packetHttpRes.setMsg(ReturnCode.param_false_message+StringUtils.substringBetween(StringUtils.substringAfter(eMessage, ":"), "--", "--"));}else{...................}packetHttpRes.setData(null);return packetHttpRes;
}

注意:因为全局捕获处理中有"–“的判断,所以所有校验注解的message值前后都得添加”–"

//正确例子
@NotNull(message = "--IsQueryBySelf不能为空--")//错误例子
@NotNull(message = "IsQueryBySelf不能为空")

3、异常处理规范

  1. 【强制】Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等等。

    说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不

    通过 catch NumberFormatException 来实现。

    正例:

    if (obj != null) {...
    }
    

    反例:

    try { obj.method();
    }
    catch (NullPointerException e) {…
    }
    
  2. 【强制】异常不要用来做流程控制,条件控制。

    说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

  3. 【强制】catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

    说明:对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题, 这是一种不负责任的表现。

    正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密码过于简单,在程 序上作出分门别类的判断,并提示给用户。

  4. 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。

  5. 【强制】事务场景中,抛出异常被 catch 后,如果需要回滚,一定要注意手动回滚事务。

  6. 【强制】finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。

    说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。
    try-with-resources语句能够帮你自动调用资源的close()函数关闭资源不用到finally块。

    static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br = new BufferedReader(new FileReader(path))) {return br.readLine();}
    }
    
  7. 【强制】不要在 finally 块中使用 return。

    说明:try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉try 块中的返回点。

    反例:

    private int x = 0;
    public int checkReturn() { try {// x 等于 1,此处不返回 return ++x; } finally { // 返回的结果是 2 return ++x; }
    }
    
  8. 【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。

    说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

4、日志规范

  1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 (SLF4J、JCL–Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和 各个类的日志处理方式统一。

    说明:日志框架(SLF4J、JCL–Jakarta Commons Logging)的使用方式(推荐使用 SLF4J)

    // SLF4J:
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Test.class);
    // JCL:
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    private static final Log log = LogFactory.getLog(Test.class);
    
  2. 【强制】所有日志文件至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。对于 当天日志,以“应用名.log”来保存,保存在/home/admin/应用名/logs/目录下, 过往日志格式为: {logname}.log.{保存日期},日期格式:yyyy-MM-dd

    说明:以 mppserver 应用为例,日志保存在/home/admin/mppserver/logs/mppserver.log,历史日志 名称为 mppserver.log.2016-08-01

  3. 【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:

    appName_logType_logName.log。logType:日志类型,如 stats/monitor/access 等;logName:日志描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。

    说明:推荐对日志进行分类,如将错误日志和业务日志分开存放,便于开发人员查看,也便于通过日志对系 统进行及时监控。

    正例:mppserver 应用中单独监控时区转换异常,如:mppserver_monitor_timeZoneConvert.log

  4. 【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。

    说明:因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符仅 是替换动作,可以有效提升性能

    正例:

    logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
    
  5. 【强制】对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。 说明:虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时(Slf4j 的常见实现 Log4j 和 Logback),就直接 return,但是参数可能会进行字符串拼接运算。此外,如果 debug(getName()) 这种参数内有 getName()方法调用,无谓浪费方法调用的开销。

    正例:

    // 如果判断为真,那么可以输出 trace 和 debug 级别的日志
    if (logger.isDebugEnabled()) { logger.debug("Current ID is: {} and name is: {}", id, getName());
    }
    
  6. 【强制】避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。

    正例:

    <logger name="com.taobao.dubbo.config" additivity="false">
    
  7. 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过 关键字 throws 往上抛出。

    正例:

    logger.error(各类参数或者对象toString() + "_" + e.getMessage(), e);
    

公司项目JAVA开发规范总结(三)——异常处理篇相关推荐

  1. Java开发规范之代码格式篇(上)

    在程序员的世界里有两件最讨厌的事情,第一件事情是讨厌写代码注释,第二件事情是讨厌看别人的代码不写注释.虽然这只是个段子,但也反映了当下很多程序员的心声.下面简单介绍下代码规范的重要性,第一,规范的代码 ...

  2. Java job interview:公司项目Java开发走进软件世界

    软件(中国大陆及香港用语,台湾称作软体,英文:Software)是一系列按照特定顺序组织的计算机数据和指令的集合.一般来讲软件被划分为系统软件.应用软件和介于这两者之间的中间件.软件并不只是包括可以在 ...

  3. Java job interview:公司项目Java开发Backbone.js为复杂WEB应用程序提供模型

    BACKBONE:网络的骨干,通常要求具有更高的带宽和更高的可靠性. 在动漫中 翻译成反骨仔 就是叛逆的小孩子(事实上是介于小孩子与成年人之间的那个年纪的人) e.g.最典型的就是路飞(Luffy)了 ...

  4. Java开发规范整理

    Java开发规范整理 (参考<Java开发手册嵩山版>) 文章目录 Java开发规范整理 一.编程规约 (一)命名 (二)常量定义 (三)代码格式 (四)OOP面向对象程序设计 (五)时间 ...

  5. Java开发规范及注意事项

    文章目录 Java开发规范及注意事项 编程规约 异常日志违约 单元测试规约 工程结构规约 数据库规约 Java开发规范及注意事项 编程规约 POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解 ...

  6. Java开发规范(阿里+腾讯)

    如何适应企业的标准化开发? 文章目录 前言 腾讯开发规范整理(精简) 阿里开发规范整理(精简) 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越 ...

  7. JAVA 开发规范标准(集合)

    JAVA 开发规范 一.编程规约 (一)命名规约 1. [强制] 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符 号结束. 反例:_name / __name / $Object / ...

  8. 谈谈阿里与谷歌的Java开发规范

    无规矩不成方圆,编码规范就如同协议,有了Http.TCP等各种协议,计算机之间才能有效地通信,同样的,有了一致的编码规范,程序员之间才能有效地合作.道理大家都懂,可现实中的我们,经常一边吐槽别人的代码 ...

  9. 谈谈ali与Google的Java开发规范

    无规矩不成方圆,编码规范就如同协议,有了Http.TCP等各种协议,计算机之间才能有效地通信,同样的,有了一致的编码规范,程序员之间才能有效地合作.道理大家都懂,可现实中的我们,经常一边吐槽别人的代码 ...

最新文章

  1. A Sequence-Based Novel Approach for Quality Evaluation of Third-Generation Sequencing Reads
  2. CLLocationCoordinate2D 用法 和一个最隐蔽的错误
  3. php输出数据过大,PHPExcel导出数据量过大处理
  4. 从零开始编写深度学习库(四)Eigen::Tensor学习使用及代码重构
  5. MyBatis深入(2)-项目结构
  6. kaike的FLAGs
  7. openstack pike版本安装笔记8(Orchestration Server:heat组件,模板服务)
  8. Python模块-decimal
  9. 队列管理器连接数设置_详解!基于Redis解决业务场景中延迟队列的应用实践,你不得不服啊...
  10. Kubernetes 小白学习笔记(9)--搭建一个kubernetes集群--安装Ubuntu虚拟机和容器docker
  11. 自定义控件设置宽度_自定义View开篇,必须跨过的一道坎儿
  12. 层次聚类——自底向上方法
  13. html+css 背景图片铺满并居中
  14. 计算机应用程序没声音,电脑没有声音怎么办,五个步骤解决电脑没声音
  15. 【python】给excel加密
  16. 新版雨尘SEO静态页面生成系统源码PHP源码
  17. Cubase10.5稳定版安装包+安装教程
  18. 如何控制鼠标和键盘的操作
  19. 王者荣耀签到系统策划案
  20. js 获取设备或浏览器唯一标识的方式

热门文章

  1. 巴特沃斯、切比雪夫Ⅰ型、切比雪夫Ⅱ型、椭圆滤波器代码
  2. 华信mysql_华信Mysql数据库修复工具
  3. 毕业论文参考文献规范格式
  4. 华硕系统安装程序正在启动服务器,华硕电脑用u盘如何装系统win7
  5. 上海蓝光集团ERP系统解决方案
  6. Edge浏览器查看md文件
  7. 设机器数字长为8位(含1位符号位在内),写出对应下列各真值的原码、补码和反码。 -26/128,82
  8. 北大青鸟课程适合高中生学习吗?
  9. 应用系统间数据传输方式总结
  10. Oracle 19C优化器中自动使用了filter操作