zoukankan      html  css  js  c++  java
  • 如何通过aop+spel表达式玩转出不一样的切面实现

    前言

    在介绍正文前,我们先来讲下spel

    什么是spel

    Spring表达式语言(简称“ SpEL”)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。

    语言语法类似于Unified EL,但是提供了其他功能,最著名的是方法调用和基本的字符串模板功能。

    此外它并不直接与Spring绑定,而是可以独立使用

    spel可以支持哪些功能

    • 文字表达式
    • 布尔运算符和关系运算符
    • 常用表达式
    • 类表达式
    • 访问属性,数组,列表和映射
    • 方法调用
    • 关系运算符
    • 分配
    • 调用构造函数
    • Bean引用
    • 数组构造
    • 内联列表
    • 内联Map
    • 三元运算符
    • 变量
    • 用户定义的功能
    • 集合投影
    • 集合选择
    • 模板表达式

    上述的spel语法可以通过如下链接进行查阅
    https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref

    spel解析基本流程

    形如下图
    spel解析流程.png
    大体的步骤如下

    1. 创建解析器
    2. 解析表达式
    3. 构造上下文
    4. 求值

    spel核心接口介绍

    1、org.springframework.expression.ExpressionParser

    表达式解析器,其功能主要是将字符串表达式转换为Expression对象。支持解析模板以及标准表达式字符串

    其默认实现为

    org.springframework.expression.spel.standard.SpelExpressionParser
    

    2、org.springframework.expression.EvaluationContext

    spel计算表达式值的“上下文”,这个Context对象可以包含多个对象,但只能有一个root(根)对象。当表达式中包含变量时,spel会根据EvaluationContext中的变量的值对表达式进行计算。可以使用setRootObject方法来设置根对象,使用setVariable方法来注册自定义变量,使用registerFunction来注册自定义函数。

    其默认实现为

    org.springframework.expression.spel.support.StandardEvaluationContext
    

    3、org.springframework.expression.Expression

    代表一个表达式,通过getValue方法根据上下文获得表达式值

    其默认实现为

    org.springframework.expression.spel.standard.SpelExpression
    

    spel官方文档

    https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions

    正文

    前边简要介绍一下spel,下边我们就通过一个小例子来演示下。

    该小例子主要是通过AOP+SPEL来实现,例子场景是:当产品价格大于10时,放入本地缓存,并通过定时器打印出本地缓存的值

    1、业务逻辑实现核心代码

    @Service
    public class ProductServiceImpl implements ProductService {
    
    
    
        @Autowired
        private ProductMockDao productMockDao;
    
        @Override
        @LocalCacheable(key = "#product.id",condition = "#product.price ge 10")
        public Product save(Product product) {
            return productMockDao.save(product);
        }
    
    
    }
    
    

    2、aop切面编写

    @Component
    @Aspect
    public class CacheAspect {
    
    
        @Around("@annotation(localCacheable)")
        public Object around(ProceedingJoinPoint pjp, LocalCacheable localCacheable) throws Throwable{
            MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
            Method method = methodSignature.getMethod();
            Object[] args = pjp.getArgs();
            Object result = pjp.proceed();
            String key = pjp.getTarget().getClass().getName() + "_" + method.getName() + "_" + args.length;
    
            if(!StringUtils.isEmpty(localCacheable.key())){
               key = SpELParserUtils.parse(method,args,localCacheable.key(),String.class);
            }
    
            System.out.println("key:"+key);
    
            if(!StringUtils.isEmpty(localCacheable.condition())){
                boolean condition = SpELParserUtils.parse(method,args,localCacheable.condition(),Boolean.class);
                if(condition){
                    LocalCache.INSTANCE.put(key,result);
                }
            }else{
                LocalCache.INSTANCE.put(key,result);
            }
    
            return result;
    
        }
    }
    

    3、解析spel核心工具类

    @Slf4j
    public final class SpELParserUtils {
    
        private static final String EXPRESSION_PREFIX = "#{";
    
        private static final String EXPRESSION_SUFFIX = "}";
    
        /**
         * 表达式解析器
         */
        private static ExpressionParser expressionParser = new SpelExpressionParser();
    
        /**
         *  参数名解析器,用于获取参数名
         */
        private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    
    
    
        private SpELParserUtils(){}
    
        /**
         * 解析spel表达式
         *
         * @param method 方法
         * @param args 参数值
         * @param spelExpression  表达式
         * @param clz  返回结果的类型
         * @param defaultResult 默认结果
         * @return 执行spel表达式后的结果
         */
        public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz, T defaultResult) {
            String[] params = parameterNameDiscoverer.getParameterNames(method);
            EvaluationContext context = new StandardEvaluationContext();
            //设置上下文变量
            for (int i = 0; i < params.length; i++) {
                context.setVariable(params[i], args[i]);
            }
            T result = getResult(context,spelExpression,clz);
            if(Objects.isNull(result)){
                return defaultResult;
            }
            return result;
        }
    
        /**
         * 解析spel表达式
         *
         * @param method  方法
         * @param args 参数值
         * @param spelExpression  表达式
         * @param clz  返回结果的类型
         * @return 执行spel表达式后的结果
         */
        public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz) {
            String[] params = parameterNameDiscoverer.getParameterNames(method);
            EvaluationContext context = new StandardEvaluationContext();
            //设置上下文变量
            for (int i = 0; i < params.length; i++) {
                context.setVariable(params[i], args[i]);
            }
            return getResult(context,spelExpression,clz);
        }
    
        /**
         * 解析spel表达式
         *
         * @param param  参数名
         * @param paramValue 参数值
         * @param spelExpression  表达式
         * @param clz  返回结果的类型
         * @return 执行spel表达式后的结果
         */
        public static <T> T parse(String param, Object paramValue, String spelExpression, Class<T> clz) {
            EvaluationContext context = new StandardEvaluationContext();
            //设置上下文变量
            context.setVariable(param, paramValue);
            return getResult(context,spelExpression,clz);
        }
    
    
        /**
         * 解析spel表达式
         *
         * @param param 参数名
         * @param paramValue 参数值
         * @param spelExpression  表达式
         * @param clz  返回结果的类型
         * @param defaultResult 默认结果
         * @return 执行spel表达式后的结果
         */
        public static <T> T parse(String param, Object paramValue,String spelExpression, Class<T> clz, T defaultResult) {
            EvaluationContext context = new StandardEvaluationContext();
            //设置上下文变量
            context.setVariable(param, paramValue);
            T result = getResult(context,spelExpression,clz);
            if(Objects.isNull(result)){
                return defaultResult;
            }
            return result;
    
        }
    
    
        /**
         * 获取spel表达式后的结果
         *
         * @param context 解析器上下文接口
         * @param spelExpression  表达式
         * @param clz  返回结果的类型
         * @return 执行spel表达式后的结果
         */
        private static <T> T getResult(EvaluationContext context,String spelExpression, Class<T> clz){
            try {
                //解析表达式
                Expression expression = parseExpression(spelExpression);
                //获取表达式的值
                return expression.getValue(context, clz);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
            }
            return null;
        }
    
    
        /**
         * 解析表达式
         * @param spelExpression spel表达式
         * @return
         */
        private static Expression parseExpression(String spelExpression){
            // 如果表达式是一个#{}表达式,需要为解析传入模板解析器上下文
            if(spelExpression.startsWith(EXPRESSION_PREFIX) && spelExpression.endsWith(EXPRESSION_SUFFIX)){
                return expressionParser.parseExpression(spelExpression,new TemplateParserContext());
            }
    
            return expressionParser.parseExpression(spelExpression);
        }
    
    }
    

    4、 示例效果

    image.png

    总结

    spel在spring应用中随处可见,比如@cacheable、@Value等,我们也可以通过aop+spel实现出适合我们业务场景的功能

    demo链接

    https://github.com/lyb-geek/springboot-learning/tree/master/springboot-aop-spel

  • 相关阅读:
    SD卡测试
    测试人员可能会遇到的问题
    HDU 1024 Max Sum Plus Plus
    HDU 1176 免费馅饼
    HDU 1257 最少拦截系统
    HDU 1087 Super Jumping! Jumping! Jumping!
    poj 1328 Radar Installation
    poj 1753 Flip Game
    HDU 1003 Max Sum
    HDU 5592 ZYB's Premutation(BestCoder Round #65 C)
  • 原文地址:https://www.cnblogs.com/linyb-geek/p/14628180.html
Copyright © 2011-2022 走看看