为spring的controller做一个打印日志、验证参数、提供默认异常处理的切面

大纲:

  1. 需求
  2. 实现
  3. 使用

一、需求

 使用spring的controller时候,有很多重复性操作,可以做一个业务轮子统一实现这些功能。

1.打印日志:调用接口所属的类、方法名称、接口入参、出参、异常、接口调用时间等信息,出入参对象需要重写toString方法
2.请求参数校验,无需使用@valid注解,支持分组校验
3.提供系统业务异常处理,其他异常默认响应500,服务器异常。

二、实现

首先定义一个用于方法注解,用于controller的方法上

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RestRequestHelper {
    Class[] validGroups() default {}; //参数校验的分组
}

然后定义切面around这个自定义的注解

代码中Result(响应结果)、ResultCodeEnum(响应码、响应码解释)、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();
        log.info("processing request:{}.{} begin,params:{}", className, methodName, args);
        long startTime = System.currentTimeMillis();
        Object proceed = null;
        if ((proceed = validParams(args,restRequestHelper.validGroups())) == null) {
            try {
                proceed = pjp.proceed();
            } catch (Throwable throwable) {
                log.error("processing request error:", throwable);
                if (throwable instanceof BaseException) {
                    BaseException baseException = (BaseException) throwable;
                    proceed = new Result<>(baseException.getErrorCode(), baseException.getMessage());
                } else {
                    proceed = new Result<>(ResultCodeEnum.SYSTEM_ERROR.getCode(), ResultCodeEnum.SYSTEM_ERROR.getMsg());
                }
            }
        }
        long endTime = System.currentTimeMillis();
        //响应参数toString
        String response = null;
        if (proceed instanceof Result) {
            Result result = (Result) proceed;
            response = result.toJson();
        } else {
            response = proceed.toString();
        }
        log.info("processing request:{}.{} end,params:{},response:{},use:{}ms", className, methodName, args, response, endTime - startTime);
        return proceed;
    }

    /**
     * 参数校验
     *
     * @param args
     * @param classes
     * @return
     */
    private Result validParams(Object[] args, Class[] classes) {
        Result errorResult = null;
        for (Object arg : args) {
            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(ResultCodeEnum.INVALID_REQUEST.getMsg());
                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(ResultCodeEnum.INVALID_REQUEST.getCode(), errorMsg.toString());
            }
        }
        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

猜你喜欢

转载自www.cnblogs.com/liuboyuan/p/12101944.html