开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情
1.Spring Validation
Web接口校验-单参数
类上需加注解@Validated
@RestController
@Validated
@RequestMapping("/validation")
public class testController {
@GetMapping("/test1")
public ResultEntity<Void> test1(@NotNull(message = "id不能为空") String id) {
return ResultEntity.success(null);
}
}
Web接口校验-对象参数
在对象内需要校验的参数上加注解
如果对象内部有嵌套对象,并且需要校验嵌套对象的内部属性,需要在嵌套对象上加@Valid注解
@RestController
@Validated
@RequestMapping("/validation")
public class testController {
@GetMapping("/test2")
public ResultEntity<Void> test2(@Validated TradeGoodsPageQuery query) {
return ResultEntity.success(bigScreenService.testCache(query));
}
}
@Data
public class GoodsQuery implements Serializable {
private static final long serialVersionUID = -4738695863328719832L;
@NotNull(message = "id不能为空")
private Long id;
@Valid
@NotNull
private TradeGoodsPageQuery query;
}
Dubbo接口校验
dubbo接口参数校验也是在参数对象中添加校验注解,但dubbo的参数校验即可以在服务端做,也可以在消费端做,在服务端开启参数校验如下
@DubboService(validation = "true")
@Component("ITradeGoodService")
public class ITradeGoodServiceImpl implements ITradeGoodService {
}
@Reference(validation = "true")
private ITradeGoodService iTradeGoodService;
建议服务端必须开启,保障业务代码正常运行。消费端尽量开启,提前在消费端进行校验可以减少一次rpc消耗
添加全局异常处理
MethodArgumentNotValidException、ConstraintViolationException、ValidationException都是参数校验中可能抛出的异常
@ControllerAdvice
public class CustomExceptionHandle {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultEntity handle(HttpServletRequest request , Exception e) {
//开始处理异常
if (e instanceof CustomException) {
// 省略
} else if (e instanceof BindException || e instanceof MethodArgumentNotValidException) {
StringBuilder sb = new StringBuilder("参数错误:[");
BindingResult result;
if (e instanceof BindException) {
BindException exception = (BindException) e;
result = exception.getBindingResult();
} else {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
result = exception.getBindingResult();
}
List<ObjectError> list = result.getAllErrors();
for (ObjectError item : list) {
sb.append(item.getDefaultMessage()).append(',');
}
sb.deleteCharAt(sb.length() - 1);
sb.append(']');
String msg = sb.toString();
LogHelper.warn(LogHelper.getLogMsg() +"入参校验异常,请求地址:{}," , request.getRequestURI() , e);
return ResultTemplate.error(-1, msg);
} else if (e instanceof ConstraintViolationException) {
LogHelper.warn("入参校验异常,请求地址:{}," , request.getRequestURI() , e);
return ResultTemplate.error(-1, e.getMessage());
} else if (e instanceof ValidationException) {
LogHelper.warn("入参校验异常,请求地址:{}," , request.getRequestURI() , e);
return ResultTemplate.error(-1, "远程调用参数错误");
} else {
// 省略
}
}
}
2.自定义慢日志打印
目的
通过阿里云平台的慢日志统计和druid自带的慢日志打印,只能打印出执行慢的sql语句,很多时候发现并不能在代码中找到完全匹配的sql语句和调用方法,导致无从优化,因此希望可以扩展druid,自定义慢日志的输出
方式
由原本的日志打印类,得知druid的慢日志打印在StatFilter中,druid通过这个类进行sql执行的时长计算和统计,超出慢sql配置的阙值时,打印error日志。
而druid的过滤器链也是可拔插的形式,我们通过配置spring.datasource.druid.filters=stat, wall 使得StatFilter生效
因此我们只需要自定义一个CustomStatFilter类,完全复制StatFilter的代码,修改我们需要打印日志的方式,然后在配置中替换掉StatFilter即可
原本的慢日志打印:
修改为
打印出堆栈信息可以方便定位到具体的问题代码
修改完成后,需要替换spring.datasource.druid.filters配置,我们发现这里配置的是别名,druid是通过别名映射将stat映射到StatFilter,这个配置文件在druid的jar包中META-INF文件夹下的druid-filter.properties中
因此我们需要在业务工程的META-INF文件夹下新建一个同名的配置文件,将源文件的内容拷贝进来,并在最后加上我们自定义的filter的别名和类路径
druid.filters.customStat=com.anchu.xxx.xxx.CustomStatFilter
最后替换spring.datasource.druid.filters=customStat,wall 即可