Spring使用Spel表示式獲取引數值
阿新 • • 發佈:2021-01-30
一、依賴
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
其他參考: