大綱:
- 需求
- 實現
- 使用
一、需求
使用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