一、依賴
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4 </dependency> 5 6 <!-- aop --> 7 <dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-aop</artifactId> 10 </dependency>
二、注解
1 @Target(ElementType.METHOD) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 public @interface Limiter { 5 /** 6 * 使用spel表達式獲取限流的key 7 * @return 8 */ 9 String value(); 10 }
三、AOP切面的應用
1 @Aspect 2 @Component 3 public class LimiterAspect { 4 private ExpressionParser parser = new SpelExpressionParser(); 5 private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); 6 7 @Pointcut("@annotation(limiter)") 8 public void pointcut(Limiter limiter) { 9 } 10 11 @Around("pointcut(limiter)") 12 public Object around(ProceedingJoinPoint pjp, Limiter limiter) throws Throwable { 13 Method method = this.getMethod(pjp); 14 String methodName = method.toString(); 15 16 //獲取方法的參數值 17 Object[] args = pjp.getArgs(); 18 EvaluationContext context = this.bindParam(method, args); 19 20 //根據spel表達式獲取值 21 Expression expression = parser.parseExpression(limiter.value()); 22 Object key = expression.getValue(context); 23 //打印 24 System.out.println(key); 25 26 return pjp.proceed(); 27 } 28 29 /** 30 * 獲取當前執行的方法 31 * 32 * @param pjp 33 * @return 34 * @throws NoSuchMethodException 35 */ 36 private Method getMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException { 37 MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); 38 Method method = methodSignature.getMethod(); 39 Method targetMethod = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes()); 40 return targetMethod; 41 } 42 43 /** 44 * 將方法的參數名和參數值綁定 45 * 46 * @param method 方法,根據方法獲取參數名 47 * @param args 方法的參數值 48 * @return 49 */ 50 private EvaluationContext bindParam(Method method, Object[] args) { 51 //獲取方法的參數名 52 String[] params = discoverer.getParameterNames(method); 53 54 //將參數名與參數值對應起來 55 EvaluationContext context = new StandardEvaluationContext(); 56 for (int len = 0; len < params.length; len++) { 57 context.setVariable(params[len], args[len]); 58 } 59 return context; 60 }
四、Controller
1 @RestController 2 public class TestController { 3 4 //獲取名為id的參數 5 @Limiter("#id") 6 @GetMapping("test") 7 public String test(Long id){ 8 return "hello"; 9 } 10 }
五、獲取對象(補充)
1、注解
1 @Limter(value = "#testObj") 2 public JSONObject test01(TestObj testObj){ 3 // ...... 4 }
多個切點同時獲取
1 /** 2 * 設置操作日志切入點 記錄操作日志 在注解的位置切入代碼 3 */ 4 //@Pointcut("@annotation(com.test.Limter)") 5 @Pointcut("@annotation(limter)") 6 public void operLogPointCut(Limter limter) { 7 } 8 9 @Pointcut("execution(public * com.test.aaa..*.*(..))") 10 public void operLogPointMethod() { 11 }
線程變量的使用,當前切面類中使用線程變量存儲變量
1 @Aspect 2 @Component 3 public class LogAspect { 4 5 @Autowired 6 private LogsService logsService; 7 // 線程變量 8 private ThreadLocal<String> threadLocal = new ThreadLocal<>(); 9 10 /** 11 * 設置操作日志切入點 記錄操作日志 在注解的位置切入代碼 12 */
方法體中存入數據
public void savaData(){ threadLocal.set("asdf"); }
在另一個方法體中獲取當前線程數據
public void savaData(){ String value = threadLocal.get(); }
切點多條件限制 &&
1 @AfterReturning(value = "operLogPointCut(limter) && operLogPointMethod()", returning = "returnValue") 2 public void saveOperLog(JoinPoint joinPoint, Limter limter, Object returnValue) { 3 Object[] args = joinPoint.getArgs(); 4 // 從切面織入點處通過反射機制獲取織入點處的方法 5 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 6 // 獲取切入點所在的方法 7 Method method = signature.getMethod(); 8 EvaluationContext context = this.bindParam(method, args); 9 Expression expression = parser.parseExpression(limter.value()); 10 11 TestObj testObj= expression.getValue(context, TestObj.class); 12 // ...... 13 new Thread(() -> logsService.saveLogs(Obj...)).start(); 14 15 // 存入數據庫后移除當前線程變量 16 threadLocal.remove(); 17 }
參考:
https://blog.csdn.net/weixin_45052750/article/details/105742934
其他參考: