zoukankan      html  css  js  c++  java
  • Spring 缓存注解 SpEL 表达式解析

    缓存注解上 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();
        }
    }
    
  • 相关阅读:
    HotSpot 虚拟机垃圾回收算法实现
    多线程死锁的产生原因以及如何避免
    Java异常实战——OutOfMemoryError
    MySQL数据库中的四种隔离级别
    Java运行时数据区概述
    MySQL 字符集和校对
    debezium关于cdc的使用(下)
    debezium关于cdc的使用(上)
    对xxl-job进行simpleTrigger并动态创建任务扩展
    折腾Java设计模式之单例模式
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10322609.html
Copyright © 2011-2022 走看看