緩存注解上 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();
}
}