Spring使用Spel表达式获取参数值
一、依赖
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
其他参考: