為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(響應結果)、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[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] 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[i] = args[i];
        }
        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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM