大纲:
- 需求
- 实现
- 使用
一、需求
使用spring的controller时候,有很多重复性操作,可以做一个业务轮子统一实现这些功能。
1.打印日志:调用接口所属的类、方法名称、接口入参、出参、异常、接口调用时间等信息,出入参对象需要重写toString方法 2.请求参数校验,无需使用@valid注解,支持分组校验 3.提供系统业务异常处理,其他异常默认响应500,服务器异常。
二、实现
首先定义一个用于方法注解,用于controller的方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RestRequestHelper {
Class[] validGroups() default {}; //参数校验的分组
}
然后定义切面around这个自定义的注解
代码中Result(响应结果)、BaseException(自定义业务异常)都是系统内自己定义的,根据业务需求设计即可。
@Aspect
@Component
@Slf4j
public class RequestHelperAspect {
@Autowired
private SmartValidator validator;
@Around("@annotation(restRequestHelper)")
public Object process(ProceedingJoinPoint pjp, RestRequestHelper restRequestHelper) {
Class<?> targetClass = pjp.getTarget().getClass();
String className = targetClass.getSimpleName();
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args instanceof ServletRequest || args instanceof ServletResponse || args instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments = args;
}
log.info("processing request:{}.{} begin,params:{}", className, methodName, arguments);
long startTime = System.currentTimeMillis();
Object proceed = null;
if ((proceed = validParams(arguments,restRequestHelper.validGroups())) == null) {
try {
proceed = pjp.proceed();
} catch (Throwable throwable) {
log.error("processing request error:", throwable);
//异常处理
}
}
long endTime = System.currentTimeMillis();
//响应参数toString
String response = null;
if (proceed instanceof Result) {
Result result = (Result) proceed;
response = result.toJson();
} else if (proceed!=null){
response = proceed.toString();
}
log.info("processing request:{}.{} end,params:{},response:{},use:{}ms", className, methodName, arguments, response, endTime - startTime);
return proceed;
}
/**
* 参数校验
*
* @param args
* @param classes
* @return
*/
private Result validParams(Object[] args, Class[] classes) {
Result errorResult = null;
for (Object arg : args) {
if(arg==null){
continue;
}
BindingResult bindingResult = new BeanPropertyBindingResult(arg, arg.getClass().getSimpleName());
if(classes.length>0){
validator.validate(arg, bindingResult,classes);
}else {
validator.validate(arg, bindingResult);
}
StringBuilder errorMsg = null;
Map<String, String> errorMap = null;
if (bindingResult.hasErrors()) {
errorMsg = new StringBuilder();
errorMsg.append(//异常信息);
final List<FieldError> fieldErrors = bindingResult.getFieldErrors();
errorMap = new HashMap<>(fieldErrors.size());
for (FieldError fieldError : fieldErrors) {
errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
}
errorMsg.append(JSON.toJSON(errorMap));
errorResult = new Result(...异常);
}
}
return errorResult;
}
}
三、使用
使用时候只要在controller中的方法上加上这个注解就可以了
@PostMapping("/create")
@RestRequestHelper( validGroups = {XxxForm.Create.class})//validGroups为需要校验的分组,如果不需要分组校验,不写这个参数即可
public Result create(@RequestBody XxxForm form) {
//业务处理
}
分组校验不会的参考我另一篇文章:https://www.cnblogs.com/liuboyuan/p/11093383.html |