缓存注解上 key、condition、unless 等 SpEL 表达式的解析

SpEl 支持的计算变量:
1)#ai、#pi、#命名参数【i 表示参数下标,从 0 开始】
2)#result:CachePut 操作和后处理 CacheEvict 操作都可使用
3)#root:CacheExpressionRootObject 对象

计算上下文根对象

/**
* 缓存注解 SpEL 表达式计算上下文根对象
*/
class CacheExpressionRootObject {
/**
* 有效的缓存集合
*/
private final Collection<? extends Cache> caches;
/**
* 目标方法
*/
private final Method method;
/**
* 方法参数
*/
private final Object[] args;
/**
* 目标对象
*/
private final Object target;
/**
* 目标对象 Class 类型
*/
private final Class<?> targetClass;
}

缓存计算上下文【附加方法参数和返回结果作为计算变量】

/**
* 基于方法的表达式计算上下文
* @since 4.2
*/
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
/**
* 目标方法
*/
private final Method method;
/**
* 参数数组
*/
private final Object[] arguments;
/**
* 参数名发现者
*/
private final ParameterNameDiscoverer parameterNameDiscoverer;
/**
* 参数变量是否已经加载
*/
private boolean argumentsLoaded = false; public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
ParameterNameDiscoverer parameterNameDiscoverer) {
super(rootObject);
this.method = method;
this.arguments = arguments;
this.parameterNameDiscoverer = parameterNameDiscoverer;
} @Override
@Nullable
public Object lookupVariable(String name) {
// 1)尝试从变量映射中读取指定名称的对象
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
// 2)未读到 && 方法参数未加载
if (!this.argumentsLoaded) {
// 加载方法参数
lazyLoadArguments();
this.argumentsLoaded = true;
// 再次读取
variable = super.lookupVariable(name);
}
return variable;
} protected void lazyLoadArguments() {
// 无参数则直接退出
if (ObjectUtils.isEmpty(this.arguments)) {
return;
} // 读取目标方法的所有参数名称
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
// 计算形参个数,以解析到的参数名优先【可能存在可变参数】
int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterCount());
// 实际传入的参数个数
int argsCount = this.arguments.length; for (int i = 0; i < paramCount; i++) {
Object value = null;
/**
* 实际传入的参数个数 > 形参个数
* && 将余下的所有入参都加入到数组中【目标方法最后一个参数是可变参数】
*/
if (argsCount > paramCount && i == paramCount - 1) {
value = Arrays.copyOfRange(this.arguments, i, argsCount);
}
// 读取实际参数
else if (argsCount > i) {
// Actual argument found - otherwise left as null
value = this.arguments[i];
}
/**
* 将 ai、pi、实际参数名称作为键,加入到计算变量映射中
* a0、p0 都表示第一个参数,依次类推
*/
setVariable("a" + i, value);
setVariable("p" + i, value);
if (paramNames != null) {
setVariable(paramNames[i], value);
}
}
}
} /**
* 缓存计算上下文,自动将方法参数添加为计算变量。
*/
class CacheEvaluationContext extends MethodBasedEvaluationContext {
/**
* 不可方法的变量集合
*/
private final Set<String> unavailableVariables = new HashSet<>(1);
CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
ParameterNameDiscoverer parameterNameDiscoverer) {
super(rootObject, method, arguments, parameterNameDiscoverer);
} /**
* 添加不可访问的变量名称
*/
public void addUnavailableVariable(String name) {
this.unavailableVariables.add(name);
} /**
* 读取变量的值
*/
@Override
@Nullable
public Object lookupVariable(String name) {
if (this.unavailableVariables.contains(name)) {
throw new VariableNotAvailableException(name);
}
return super.lookupVariable(name);
}
}

缓存注解上 SpEL 表达式计算器

/**
* 用于计算缓存注解上 SpEL 表达式值的工具类
*/
public abstract class CachedExpressionEvaluator {
/**
* 表达式解析器
*/
private final SpelExpressionParser parser;
/**
* 参数名称发现者
*/
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); protected CachedExpressionEvaluator(SpelExpressionParser parser) {
Assert.notNull(parser, "SpelExpressionParser must not be null");
this.parser = parser;
} protected CachedExpressionEvaluator() {
this(new SpelExpressionParser());
} protected SpelExpressionParser getParser() {
return parser;
} protected ParameterNameDiscoverer getParameterNameDiscoverer() {
return parameterNameDiscoverer;
} /**
* 将缓存注解上的 SpEL 表达式字符串解析为 Expression
*/
protected Expression getExpression(Map<ExpressionKey, Expression> cache,
AnnotatedElementKey elementKey, String expression) {
// 创建缓存键
final ExpressionKey expressionKey = createKey(elementKey, expression);
// 如果已存在则直接返回
Expression expr = cache.get(expressionKey);
if (expr == null) {
// 使用 SpelExpressionParser 解析表达式字符串
expr = getParser().parseExpression(expression);
// 写入缓存
cache.put(expressionKey, expr);
}
return expr;
} private ExpressionKey createKey(AnnotatedElementKey elementKey, String expression) {
return new ExpressionKey(elementKey, expression);
} protected static class ExpressionKey implements Comparable<ExpressionKey> {
/**
* 注解元素+目标类型
*/
private final AnnotatedElementKey element;
/**
* SpEL 表达式字符串
*/
private final String expression; protected ExpressionKey(AnnotatedElementKey element, String expression) {
Assert.notNull(element, "AnnotatedElementKey must not be null");
Assert.notNull(expression, "Expression must not be null");
this.element = element;
this.expression = expression;
} @Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ExpressionKey)) {
return false;
}
final ExpressionKey otherKey = (ExpressionKey) other;
return element.equals(otherKey.element) &&
ObjectUtils.nullSafeEquals(expression, otherKey.expression);
} @Override
public int hashCode() {
return element.hashCode() * 29 + expression.hashCode();
} @Override
public String toString() {
return element + " with expression \"" + expression + "\"";
} @Override
public int compareTo(ExpressionKey other) {
int result = element.toString().compareTo(other.element.toString());
if (result == 0) {
result = expression.compareTo(other.expression);
}
return result;
}
}
} /**
* 处理缓存 SpEL 表达式解析的工具类
*/
class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
/**
* 指示没有结果变量
*/
public static final Object NO_RESULT = new Object();
/**
* 结果变量不可使用
*/
public static final Object RESULT_UNAVAILABLE = new Object();
/**
* 计算上下文中,保存结果对象的变量名称
*/
public static final String RESULT_VARIABLE = "result";
/**
* key 表达式缓存
*/
private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<>(64);
/**
* condition 条件表达式缓存
*/
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
/**
* unless 条件表达式缓存
*/
private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<>(64); public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass, Method targetMethod,
@Nullable Object result, @Nullable BeanFactory beanFactory) {
// 创建计算上下文的根对象
final CacheExpressionRootObject rootObject = new CacheExpressionRootObject(
caches, method, args, target, targetClass);
// 创建缓存计算上下文
final CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
rootObject, targetMethod, args, getParameterNameDiscoverer());
// 1)方法返回值不可用
if (result == RESULT_UNAVAILABLE) {
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
}
// 2)方法返回值可用,则以 result 作为 key 将其加入到计算变量中
else if (result != NO_RESULT) {
evaluationContext.setVariable(RESULT_VARIABLE, result);
}
if (beanFactory != null) {
// 写入 BeanFactoryResolver
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
} /**
* 计算键的值
*/
@Nullable
public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return getExpression(keyCache, methodKey, keyExpression).getValue(evalContext);
} /**
* 计算 condition 值
*/
public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return Boolean.TRUE.equals(getExpression(conditionCache, methodKey, conditionExpression).getValue(
evalContext, Boolean.class));
} /**
* 计算 unless 值
*/
public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return Boolean.TRUE.equals(getExpression(unlessCache, methodKey, unlessExpression).getValue(
evalContext, Boolean.class));
} /**
* Clear all caches.
*/
void clear() {
keyCache.clear();
conditionCache.clear();
unlessCache.clear();
}
}

Spring 缓存注解 SpEL 表达式解析的更多相关文章

  1. Spring 缓存注解解析过程

    Spring 缓存注解解析过程 通过 SpringCacheAnnotationParser 的 parseCacheAnnotations 方法解析指定方法或类上的缓存注解, @Cacheable ...

  2. SPEL 表达式解析

    Spring Expression Language 解析器 SPEL解析过程 使用 ExpressionParser 基于 ParserContext 将字符串解析为 Expression, Exp ...

  3. Spring – 缓存注解

    Spring缓存抽象概述 Spring框架自身并没有实现缓存解决方案,但是从3.1开始定义了org.springframework.cache.Cache和org.springframework.ca ...

  4. Spring缓存注解@Cache使用

    参考资料 http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ http://swiftlet.net/archive ...

  5. 【Spring】22、Spring缓存注解@Cache使用

    缓存注解有以下三个: @Cacheable      @CacheEvict     @CachePut @Cacheable(value=”accountCache”),这个注释的意思是,当调用这个 ...

  6. Spring缓存注解@CachePut , @CacheEvict,@CacheConfig使用

    Cacheable CachePut CacheEvict CacheConfig 开启缓存注解 @Cacheable @Cacheable是用来声明方法是可缓存的.将结果存储到缓存中以便后续使用相同 ...

  7. 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用

    https://blog.csdn.net/u012240455/article/details/80844361 注释介绍 @Cacheable @Cacheable 的作用 主要针对方法配置,能够 ...

  8. Spring缓存注解@Cacheable、@CacheEvict、@CachePut使用(转)

    原文地址:https://www.cnblogs.com/fashflying/p/6908028.html 从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对 ...

  9. Spring缓存注解@Cacheable

    @Cacheable @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @Cacheable 作用和配置方法 参数 解释 example value 缓存的名称, ...

随机推荐

  1. JavaSE基础:泛型

    泛型 1.引入 情景模式描述,假设完成一个学生的成绩的情况: 整数: math=80,english=70 小数: math=85.6,englisth=77.8 字符串: math="66 ...

  2. 剑指offer-5:十进制转二进制

    一.二进制中‘1’的个数 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. public class Solution { public int NumberOf1(int n) { ...

  3. iOS常用数学常量宏

    在实际工作中有些程序不可避免的需要使用数学函数进行计算,比如地图程序的地理坐标到地图坐标的变换.Objective-C做为ANSI C的扩展,使用C标准库头文件<math.h>中定义的数学 ...

  4. 摘抄 <关于 作为>

    出路在哪里?出路在于思路! 其实,没有钱.没有经验.没有阅历.没有社会关系,这些都不可怕.没有钱,可以通过辛勤劳动去赚:没有经验,可以通过实践操作去总结:没有阅历,可以一步一步去积累:没有社会关系,可 ...

  5. zencart 输出产品特价折扣百分比

    通过调用zen_get_products_base_price($products_id)获取原价,zen_get_products_special_price($products_id)获取特价,进 ...

  6. Python之面向对象之单例模式的四种方式

    一.内容 保证一个类只有一个实例,并提供一个访问它的全局访问点 二.角色 单利 三.使用场景 当类只有一个实例而且客户可以从一个众所周知的访问点访问它时 比如:数据库链接.Socket创建链接 四.优 ...

  7. pushd&popd&dirs命令

    dirs 显示当前目录栈中的所有记录 -p  一个目录一行显示  -l  以完整格式显示  -c  删除目录栈中的所有记录  -v  每行一个目录来显示,每个目录前加上编号  +N  从左到右的第n个 ...

  8. 一种循环C字符数组的骚操作

    #include <stdio.h> #include <stdlib.h> int main() { char wenwa[] = "程劲小盆友在做什么" ...

  9. 【JZOJ2156】【2017.7.10普及】复仇者vsX战警之训练

    题目 月球上反凤凰装甲在凤凰之力附身霍普之前,将凤凰之力打成五份,分别附身在X战警五大战力上面辐射眼.白皇后.钢力士.秘客和纳摩上(好尴尬,汗). 在凤凰五使徒的至高的力量的威胁下,复仇者被迫逃到昆仑 ...

  10. TF-epoch、 iteration和batchsize区别(转载)

    from http://www.cnblogs.com/qggg/p/6876942.html 转自 http://blog.csdn.net/sinat_30071459/article/detai ...