FrameWork

简介

本文章主要记录在平时的编码中,对 SpringBoot 后台项目框架的一些优化,完善项目的功能,例如定时任务、AOP 日志切面、DTO 对象的校验等等,都是围绕着提高后台项目完善框架

Quartz

  1. Quartz 动态定时任务器,在时间的开发中,可能需要去读取书库表中的记录,然后根据 cron 表达式 去执行对应的任务
  2. 比如测试平台会需要 每天早上或指定的时间去执行我们的测试计划,这个时候就可以需要去设计动态定时定时器了

在实际的工作中,我们需要给具体的具体的任务执行代码逻辑(如果针对自动化测试,我们可以在表上加一个 plantId 这个 Id 下有所有需要执行的用例,这样执行 task 的时候把 plantId 传入进来即可)
动态定时任务展示

  1. 依赖 quartz quartz-jobs
  2. 继承 对外开放增删改查 task 的接口,编辑后,更新系统中的 job
  3. 待补充具体实现细节—>待填坑

AOP 日志切面

  1. 在平时的测试工作中,如果遇到问题是需要我们去查看后台日志,看看有什么报错,或者具体调用一个接口的时候传递的参数是什么,执行的 SQL 是什么,以及响应是什么,尤其是针对线上问题
  2. 如果有一个好的 日志打印方式 那么能极大的方便我们定位问题

通过 AOP 思想,自定义注解 @SystemLog,对我们的 Controller 增强,这样就能对我们的接口日志做到较好的交互和统一,便于问题排查,有一个良好的日志体系也是框架完善的一部分
AOP切面日志

  1. 需要依赖 spring-boot-starter-aop 通过 AOP 的思想,找到切面,注入切点
  2. 具体实现 TODO –> 挖坑代填

入参校验

介绍

  1. 在前后端分离的项目中,很多程序的校验都是放在前端,例如 vue 的项目中可以自定义表单的校验规则 rule,但是这个只能控制前端用户通过界面的输入校验,并不能控制通过接口工具时用户不合法的输入造成的接口
  2. Java API规范 (JSR303) 定义了Bean校验的标准validation-api,但没有提供实现
  3. hibernate validation是对这个规范的实现,并增加了校验注解如@Email、@Length等
  4. Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验

不做参数校验带来的问题:1️⃣ 入库异常(数据库字段长度为50,实际数据长度为100)2️⃣ NPE异常(未接收到参数就进行调用)3️⃣ 通过 if else 硬编码 方式会随着校验参数的个数增多而增多,不利于代码阅读(500行代码,250行都在用if…else做参数校验 4️⃣ 需要重复校验,调用不同的接口,传入的参数比较类似,需要在不同的接口重新校验一遍
做参数校验的好处:1️⃣ 减少已知可规避的风险 2️⃣ 减少代码量,工作量

if-else进行参数校验

参数校验效果展示

实现

  1. 如果 SpringBoot 版本小于2.3.x,spring-boot-starter-web 会自动传入hibernate-validator依赖
  2. 如果 SpringBoot 版本大于2.3.x,则需要手动引入依赖
  3. 当前项目为 SpringBoot 2.6.4 ,通过 gradle 构建
1
implementation("org.hibernate:hibernate-validator:8.0.0.CR2")

引入 hibernate-validator

对入参 HospitalConfigDTO 对象的 hospitalCode 字段进行参数校验,限定为 3-9 位
参数校验示例
注意这里返回的状态码是 400 还有响应体格式很乱,这个后面需要统一优化
响应的提示信息不同的注解会有对应的默认 message,也可自行在注解上添加 @Size(min = 3,max=9,message = "hospitalCode 字段的长度需要控制在 3-9 位")
请求测试

  1. 前面的接口响应结果其实不是我们想要的,我们需要的是能像正常的接口一样返回也就是我们上面的效果展示中的那样,所以我们需要去做异常拦击,然后抛出我们的自定义异常
  2. 以下为自定义 MethodArgumentNotValidException 异常的核心代码
  3. 注意自定义异常的时候要在类上加上 @RestControllerAdvice 注解,@RestControllerAdvice 注解是对 @ControllerAdvice 注解的增强,默认会给每个方法加上 @ResponseBody 注解
MethodArgumentNotValidException 异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @param e 拦截 MethodArgumentNotValidException 类型的异常
* @return 统一返回对象
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultObject<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("[全局异常处理] [参数校验不通过]{}", e.getMessage(), e);
return ResultObject.<Object>builder()
.success(false)
.code(ResultCodeEnum.PARAM_NOT_VALID.getCode())
.message(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage())
.map("args", e.getBindingResult().getTarget())
.build();
}
ConstraintViolationException 异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 方法RequestParam/PathVariable形式参数校验异常
*
* @param e 处理 ConstraintViolationException 类型的异常
* @return 统一返回对象
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResultObject<String> handleConstraintViolationException(ConstraintViolationException e) {
log.error("[全局异常处理] [参数校验不通过]{}", e.getMessage(), e);
return ResultObject.<String>builder()
.success(false)
.code(ResultCodeEnum.PARAM_NOT_VALID.getCode())
.message(e.getMessage())
.build();
}
UnexpectedTypeException 异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
/**  
* 方法参数校验异常[类型不配备]
* * @param e 处理 UnexpectedTypeException 类型的异常
* @return 统一返回对象
*/
@ExceptionHandler(UnexpectedTypeException.class)
public ResultObject<String> handleUnexpectedTypeException(UnexpectedTypeException e) {
log.error("[全局异常处理] [参数校验类型不匹配]{}", e.getMessage(), e);
return ResultObject.builder(e.getMessage()).success(false)
.code(ResultCodeEnum.PARAM_TYPE_ERROR.getCode())
.message(ResultCodeEnum.PARAM_TYPE_ERROR.getMessage())
.build();
}

Logback

当项目上线后,用户执行程序出现异常,就会触发邮件通知到指定的人员邮箱中,也属于测试后移的一种思想吧,对线上程序的监控,发现问题根据情况决定是否紧急处理
Logback邮件提示

TODO

  1. 具体实现主要是通过配置,然后要在 xml 文件中写一写规则
  2. 除了邮件提示,一般日志的归档也是通过这个来实现的