如何經過aop+spel表達式玩轉出不同的切面實現

前言

在介紹正文前,咱們先來說下spelhtml

什麼是spel

Spring表達式語言(簡稱「 SpEL」)是一種功能強大的表達式語言,支持在運行時查詢和操做對象圖。java

語言語法相似於Unified EL,可是提供了其餘功能,最著名的是方法調用和基本的字符串模板功能。git

此外它並不直接與Spring綁定,而是能夠獨立使用github

spel能夠支持哪些功能

  • 文字表達式
  • 布爾運算符和關係運算符
  • 經常使用表達式
  • 類表達式
  • 訪問屬性,數組,列表和映射
  • 方法調用
  • 關係運算符
  • 分配
  • 調用構造函數
  • Bean引用
  • 數組構造
  • 內聯列表
  • 內聯Map
  • 三元運算符
  • 變量
  • 用戶定義的功能
  • 集合投影
  • 集合選擇
  • 模板表達式

上述的spel語法能夠經過以下連接進行查閱
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-refspring

spel解析基本流程

形以下圖
spel解析流程.png
大致的步驟以下express

  1. 建立解析器
  2. 解析表達式
  3. 構造上下文
  4. 求值

spel核心接口介紹

一、org.springframework.expression.ExpressionParser

表達式解析器,其功能主要是將字符串表達式轉換爲Expression對象。支持解析模板以及標準表達式字符串數組

其默認實現爲緩存

org.springframework.expression.spel.standard.SpelExpressionParser
二、org.springframework.expression.EvaluationContext

spel計算表達式值的「上下文」,這個Context對象能夠包含多個對象,但只能有一個root(根)對象。當表達式中包含變量時,spel會根據EvaluationContext中的變量的值對錶達式進行計算。能夠使用setRootObject方法來設置根對象,使用setVariable方法來註冊自定義變量,使用registerFunction來註冊自定義函數。springboot

其默認實現爲ide

org.springframework.expression.spel.support.StandardEvaluationContext
三、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時,放入本地緩存,並經過定時器打印出本地緩存的值

一、業務邏輯實現核心代碼
@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);
    }


}
二、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;

    }
}
三、解析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);
    }

}
四、 示例效果

image.png

總結

spel在spring應用中隨處可見,好比@cacheable、@Value等,咱們也能夠經過aop+spel實現出適合咱們業務場景的功能

demo連接

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

相關文章
相關標籤/搜索