一:自定義注解類
1 package com.wing.my.cloud.system.modular.system.util.annotation; 2 3 import java.lang.annotation.*; 4 5 @Target({ElementType.PARAMETER, ElementType.METHOD}) 6 @Retention(RetentionPolicy.RUNTIME) 7 @Documented 8 public @interface CheckNullParams { 9 String[] params(); 10 }
二:AOP
1 package com.wing.my.cloud.system.modular.system.util.aspect; 2 3 import com.alibaba.fastjson.JSON; 4 import com.alibaba.fastjson.JSONObject; 5 import com.alibaba.fastjson.serializer.SerializerFeature; 6 import com.wing.my.cloud.kernel.model.exception.ServiceException; 7 import com.wing.my.cloud.system.modular.system.util.annotation.CheckNullParams; 8 import lombok.extern.slf4j.Slf4j; 9 import org.aspectj.lang.ProceedingJoinPoint; 10 import org.aspectj.lang.annotation.Around; 11 import org.aspectj.lang.annotation.Aspect; 12 import org.aspectj.lang.annotation.Pointcut; 13 import org.aspectj.lang.reflect.MethodSignature; 14 import org.springframework.stereotype.Component; 15 import org.springframework.util.StringUtils; 16 import org.springframework.web.context.request.RequestContextHolder; 17 import org.springframework.web.context.request.ServletRequestAttributes; 18 19 import javax.servlet.http.HttpServletRequest; 20 import java.util.*; 21 22 /** 23 * 校驗參數不能為空 24 * ProceedingJoinPoint只能用在環繞通知上。 25 */ 26 @Slf4j 27 @Aspect 28 @Component 29 public class CheckNullParamsAspect { 30 31 private static String dateFormat = "yyyy-MM-dd HH:mm:ss"; 32 33 @Pointcut("@annotation(com.wing.my.cloud.system.modular.system.util.annotation.CheckNullParams)") 34 public void paramNotNull(){} 35 36 @Around("paramNotNull()") 37 public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{ 38 log.info("進入到環繞通知中"); 39 // 1、記錄方法開始執行的時間 40 long start = System.currentTimeMillis(); 41 42 // 2、打印請求參數 43 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 44 HttpServletRequest request = attributes.getRequest(); 45 String target = joinPoint.getSignature().getDeclaringTypeName(); // 全路徑類名 46 String classNm = target.substring(target.lastIndexOf(".") + 1, target.length()); // 類名截取 47 String method = joinPoint.getSignature().getName(); // 獲取方法名 48 Map<String, String> params = getAllRequestParam(request); // 獲取請求參數 49 log.info("{}.{} 接收參數: {}", classNm, method, JSON.toJSONString(params)); 50 51 CheckNullParams check = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CheckNullParams.class); // 獲取注解 52 String[] requiredFields = check.params(); // 獲取注解參數 53 // 3、必填參數非空校驗 54 Map<String,Object> result = validParams(params, requiredFields); 55 if ((Boolean) result.get("boolean")) { 56 57 Object object = joinPoint.proceed(); // 必填參數非空校驗通過,執行方法,獲取執行結果 58 // 4、打印應答數據和方法耗時 59 long time = System.currentTimeMillis() - start; 60 log.info("{}.{} 應答數據: {}; 耗時 {} ms", classNm, method, JSONObject.toJSONStringWithDateFormat(object, 61 dateFormat, SerializerFeature.WriteMapNullValue), time); 62 return object; 63 } else { 64 // 必填參數非空校驗未通過,拋出異常,由GlobalExceptionHandler捕獲全局異常,返回調用方“參數缺失” 65 throw new ServiceException(500, "參數【" + result.get("param") + "】缺失或格式錯誤"); 66 67 } 68 69 } 70 71 /** 72 * 校驗傳入參數params(非null)中是否必含requiredFields(非null)中的各個屬性,且屬性值非空 73 * 74 * @param params 傳入參數 75 * @param requiredFields 設置的非空屬性數組 76 * @return 校驗通過返回true,否則返回false 77 */ 78 private Map<String,Object> validParams(Map<String, String> params, String[] requiredFields) { 79 Map<String, Object> map = new HashMap<>(2); 80 if (requiredFields.length == 0) { 81 // 無必送參數,直接返回true 82 map.put("boolean", true); 83 return map; 84 } else { 85 for (String field : requiredFields) { 86 if (StringUtils.isEmpty(params.get(field))) { 87 map.put("boolean", false); 88 map.put("param", field); 89 return map; 90 } 91 } 92 map.put("boolean", true); 93 return map; 94 } 95 } 96 97 /** 98 * 獲取請求參數 99 */ 100 public static Map<String, String> getAllRequestParam(HttpServletRequest request) { 101 Map<String, String> res = new HashMap<>(); 102 Enumeration<?> temp = request.getParameterNames(); 103 if (null != temp) { 104 while (temp.hasMoreElements()) { 105 String en = (String) temp.nextElement(); 106 String value = request.getParameter(en); 107 res.put(en, value); 108 // 在報文上送時,如果字段的值為空,則不上送<下面的處理為在獲取所有參數數據時,判斷若值為空,則刪除這個字段> 109 if (StringUtils.isEmpty(res.get(en))) { 110 res.remove(en); 111 } 112 } 113 } 114 return res; 115 } 116 117 }
三:實現
1 @ApiImplicitParams({ 2 @ApiImplicitParam(name = "channelParam", value = "閑錢保渠道維護表實體類", dataType = "ChannelParam", paramType = "query", example = "xxx"), 3 }) 4 @ResponseBody 5 @ApiResource(name = "測試接口", path = "/test1") 6 @CheckNullParams(params = {"custId","inpName"}) //參數不能為空 7 public ResponseData test1( InsurancePolicyParam insurancePolicyParam) { 8 testUserService.testExceptionAop(insurancePolicyParam); 9 return ResponseData.success(); 10 }
參數 custId,inpName為InsurancePolicyParam實體類的屬性。
AOP各種通知
前置通知
方法執行前開始執行
1 @Before("declareJointPointExpression()") 2 public void beforeMethod(JoinPoint joinPoint) { 3 String methodName = joinPoint.getSignature().getName(); 4 Object[] args = joinPoint.getArgs(); 5 6 System.out.println("這是切面開始打印出來的--->The method " + methodName + " begins with " + Arrays.asList(args)); 7 }
1 //后置通知 2 //方法執行后開始執行 3 @After("declareJointPointExpression()") 4 public void afterMethod(JoinPoint joinPoint) { 5 String methodName = joinPoint.getSignature().getName(); 6 System.out.println("這是切面結束打印出來的--->The method " + methodName + " ends"); 7 }
帶返回值的后置通知
方法正常執行后執行
1 @AfterReturning(value = "declareJointPointExpression()", 2 returning = "result") 3 public void afterReturningMethod(JoinPoint joinPoint,Object result) { 4 String methodName = joinPoint.getSignature().getName(); 5 System.out.println("The method " + methodName + " ends with " + result); 6 }
異常通知
代碼執行中出現異常后執行
1 @AfterThrowing(value = "exceptionLog()",throwing = "e") 2 public void afterThrowing(JoinPoint point, Exception e) throws Throwable { 3 String target = point.getSignature().getDeclaringTypeName(); // 全路徑類名 4 String classNm = target.substring(target.lastIndexOf(".") + 1, target.length()); // 類名截取 5 String method = point.getSignature().getName(); // 獲取方法名 6 log.error("{}.{} 【異常信息】: {}", classNm, method, e.getMessage()); 7 }
先執行前置通知,然后代碼,然后異常通知,然后后置通知。
