对缓存的key进行优化:支持SPEL表达式
举例:skuInfo:#{#params[0]}
实现思路:
1、在@GmallCache注解上添加上一个cacheKey属性,让使用者显示来指定缓存的key
2、在AOP切面类中使用解析SPEL表达式,获取具体的key值
SpelExpressionParser
要想对SPEL表达式进行解析,那么此时就需要使用到SpelExpressionParser对象。
官网地址:https://docs.spring.io/spring-framework/docs/5.3.26-SNAPSHOT/reference/html/core.html#expressions
SpelExpressionParser入门
SpelExpressionParser解析表达式的时候支持运算、方法调用、静态方法调用。
@Test
publicvoidsepl01() {
//SpelExpressionParser入门
// 在Spel表达式中如果想表示一个字符串需要使用''将字符串引起了,否在就会当做成是一个变量
Stringstr01="'hello'" ;//字符串
Stringstr02="1 + 1" ;//可以进行运算
Stringstr03="'hello'.toUpperCase()" ;//可以表达式方法调用
Stringstr04="T(java.util.UUID).randomUUID().toString().replace('-' , '')" ;//可以调用静态方法
SpelExpressionParser spelExpressionParser=newSpelExpressionParser() ;
Expressionexpression=spelExpressionParser.parseExpression(str04);
Stringvalue=expression.getValue(String.class);
System.out.println(value);
}
ParserContext指定解析边界
作为一个表达式而言,有时可能只需要将其一部分通过解析器进行解析。那么此时就需要使用ParserContext定义解析器要解析的边界。
// ParserContext定义解析边界
@Test
publicvoidsepl02() {
Stringstr="skuInfo:#{1 + 1}:#{'hello'.toUpperCase()}:#{T(java.util.UUID).randomUUID().toString().replace('-' , '')}" ;
SpelExpressionParser spelExpressionParser=newSpelExpressionParser() ;
Expressionexpression=spelExpressionParser.parseExpression(str , ParserContext.TEMPLATE_EXPRESSION);//ParserContext.TEMPLATE_EXPRESSION解析格式前缀#{后缀}
Stringvalue=expression.getValue(String.class);
System.out.println(value);
}
从SpelExpressionParser上下文对象中动态获取值
上述的表达式还并不算特别灵活【比如hello字符串是直接写死的】,在SPEL表达式中支持从SpelExpressionParser上下文对象中动态获取执行。
使用思想:
1、在SpelExpressionParser上下文对象存储变量的值
2、在表达式中使用#变量名获取表达式的值
@Test
publicvoidsepl03() {
Stringstr="skuInfo:#{#params.toUpperCase()}" ;
SpelExpressionParser spelExpressionParser=newSpelExpressionParser() ;
Expressionexpression=spelExpressionParser.parseExpression(str , ParserContext.TEMPLATE_EXPRESSION);
EvaluationContext evaluationContext=newStandardEvaluationContext() ; // SPEL表达式上下文对象
evaluationContext.setVariable("params" , "hello");
Stringvalue=expression.getValue(evaluationContext , String.class); // 获取值的时候将上下文对象作为方法参数传递过去
System.out.println(value);
}
spel表达式实战
从SpelExpressionParser上下文对象中动态获取值:解析"sku:info:#{#params[0]}"
//params为获取到方法参数数组后 自定义的参数数组的名字
1.(自定义注解添加需要的属性)cacheKey
2.业务方法@GmallCache注解为自定义属性cacheKey赋值
3.创建解析spel表达式的方法
注解添加cacheKey属性**:
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.METHOD)//生效在方法上
public@interface GMallCache {
public String cacheKey() ; // 用来让使用者指定缓存的key的名称(即赋值给这个参数)
}
业务方法@GmallCache注解添加cacheKey属性:
@GmallCache(cacheKey="sku:info:#{#params[0]}")
解析spel表达式:
// SpelExpressionParser是线程安全的
private static final SpelExpressionParser spelExpressionParser=newSpelExpressionParser() ;
// 通过解析获取自定义的缓存key
public<T>TparaseExpression(ProceedingJoinPoint proceedingJoinPoint , StringcacheKey , Class<T> clazz) {
Expressionexpression=spelExpressionParser.parseExpression(cacheKey, ParserContext.TEMPLATE_EXPRESSION);
EvaluationContext evaluationContext=newStandardEvaluationContext() ;
evaluationContext.setVariable("params" , proceedingJoinPoint.getArgs());
TcacheKeyData=expression.getValue(evaluationContext, clazz);
returncacheKeyData ;
}
扩展:获取方法的返回值类型
// 获取目标方法的返回值类型
public Type getMethodType(ProceedingJoinPoint proceedingJoinPoint) {
MethodSignature methodSignature= (MethodSignature) proceedingJoinPoint.getSignature() ;
Methodmethod=methodSignature.getMethod();
TypegenericReturnType=method.getGenericReturnType();
returngenericReturnType ;
}