spel简单使用&基于注解的AOP使用SPEL获取参数的值
简单的场景是基于接口层面对数据实现权限校验。
1. Spel 简单使用
package com.example.demo; import lombok.Builder; import lombok.Data; import org.assertj.core.util.Lists; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.util.Date; import java.util.List; /** * https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/expressions.html */ public class SpelTest { public static void main(String[] args) { UserWrapper zs = UserWrapper.builder() .age(22).birthDay(new Date()).username("zs") .likes(Lists.newArrayList("篮球", "羽毛球")) .build(); System.out.println(zs); // 1. 创建解析器 ExpressionParser parser = new SpelExpressionParser(); // 2. 设置上下文 EvaluationContext context = new StandardEvaluationContext(); context.setVariable("zs", zs); context.setVariable("zs2", null); // 3. 创建表达式并且解析 // 3.1 解析username System.out.println("========0"); Expression expression = parser.parseExpression("#zs.username"); String name = (String) expression.getValue(context); System.out.println(name); String value = parser.parseExpression("#zs.username + '111222'").getValue(context, String.class); System.out.println(value); // 3.2 解析likes 爱好 System.out.println("========1"); System.out.println(parser.parseExpression("#zs.likes").getValue(context)); System.out.println(parser.parseExpression("#zs.likes").getValueType(context)); // spel 调用方法 System.out.println(parser.parseExpression("#zs.likes.size()").getValue(context)); System.out.println(parser.parseExpression("#zs.likes.size() == 2").getValue(context)); System.out.println(parser.parseExpression("#zs.likes.get(1)").getValue(context)); // spel 调用方法添加元素 System.out.println(parser.parseExpression("#zs.likes.add('新爱好')").getValue(context)); System.out.println(zs.getLikes()); // 3. 解析age 年龄 System.out.println("========1"); Integer age = parser.parseExpression("#zs.age").getValue(context, int.class); System.out.println(age); // 4. 解析在上下文环境中,值为null的获取的值为null; 不存在上下文中、或者解析值为null的对象的属性会报 SpelEvaluationException 错误 // Object value1 = parser.parseExpression("#zs2.age").getValue(context); // Object value2 = parser.parseExpression("#zs.age2").getValue(context); // System.out.println(value1); /** * UserWrapper(username=zs, age=22, birthDay=Fri Feb 10 11:01:56 CST 2023, likes=[篮球, 羽毛球]) * ========0 * zs * zs111222 * ========1 * [篮球, 羽毛球] * class java.util.ArrayList * 2 * true * 羽毛球 * true * [篮球, 羽毛球, 新爱好] * ========1 * 22 */ } } @Data @Builder class UserWrapper { private String username; private int age; private Date birthDay; private List<String> likes; }
2. AOP基于Spel 获取参数值
1. 注解
package com.example.demo.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AopAnnotation { /** * id 表达式 * @return */ String idSpel() default ""; /** * 值 * @return */ String value(); }
2. 切面Aspect
package com.example.demo.aop; import cn.hutool.core.util.ArrayUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.expression.EvaluationContext; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; @Component @Aspect public class AopAspect { // 定义一个空方法,借用其注解抽取切点表达式 @Pointcut("@annotation(com.example.demo.aop.AopAnnotation)") public void pc() { } // 环绕通知 @Around("AopAspect.pc()") public Object around(ProceedingJoinPoint pjp) { System.out.println("----------------环绕通知之前 的部分(相当于前置通知)----------------"); // 获取到类名 String targetName = pjp.getTarget().getClass().getName(); System.out.println("代理的类是:" + targetName); // 获取到参数 Object[] parameter = pjp.getArgs(); System.out.println("传入的参数是:" + Arrays.toString(parameter)); // 获取到方法签名,进而获得方法 MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); System.out.println("增强的方法名字是:" + method.getName()); // 处理一些业务逻辑 // 模拟简单的获取参数 AopAnnotation annotation = method.getAnnotation(AopAnnotation.class); String value = annotation.value(); System.out.println("注解参数值: value\t" + value); // spel 解析参数 Parameter[] parameters = method.getParameters(); String idSpel = annotation.idSpel(); if (ArrayUtil.isNotEmpty(parameters)) { ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = new StandardEvaluationContext(); for (int index = 0, length_1 = parameters.length; index < length_1; index++) { String paramterName = parameters[index].getName(); Object paramterValue = parameter[index]; context.setVariable(paramterName, paramterValue); } String value1 = parser.parseExpression(idSpel).getValue(context, String.class); System.out.println(String.format("idSpel: %s, value: %s", idSpel, value1)); } else { System.out.println("idSpel 参数获取不到上下文环境, idSpel: " + idSpel); } // 让方法执行 Object proceed = null; try { proceed = pjp.proceed(); // 环绕通知之后的业务逻辑部分 System.out.println("----------------环绕通知之后的部分(相当于后置通知AfterReturning)-----------------"); } catch (Throwable e) { System.out.println("-------------环绕通知的异常部分(相当于异常通知AfterThrowing)--------------------------"); e.printStackTrace(); } finally { System.out.println("-------------环绕通知的最终部分部分(相当于最终通知After)--------------------------"); } return proceed; } }
3. 测试Controller
package com.example.demo.aop; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/aop") public class AopTestController { @GetMapping("/") @AopAnnotation(idSpel = "#id", value = "test") public String test(String id) { return "111222"; } }
4. 访问: http://localhost:8088/aop/?id=3
5. 控制台
----------------环绕通知之前 的部分(相当于前置通知)---------------- 代理的类是:com.example.demo.aop.AopTestController 传入的参数是:[3] 增强的方法名字是:test 注解参数值: value test idSpel: #id, value: 3 ----------------环绕通知之后的部分(相当于后置通知AfterReturning)----------------- -------------环绕通知的最终部分部分(相当于最终通知After)--------------------------
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】