Spring Aop中解析spel表示式,實現更靈活的功能

weixin_34377065發表於2019-02-25

前言

在Spring Aop中,我們可以拿到攔截方法的引數,如果能結合spel表示式,就能實現更加靈活的功能。典型的實現有Spring的快取註解:

@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id) {
}
複製程式碼
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
public User save(User user) {
複製程式碼

本文介紹如何在aop程式設計中解析spel表示式,提供幾個通用的方法。

Spring使用自定義註解實現aop的方式這裡就不贅述,只著重介紹如何解析spel。

準備

實現非常簡單,Spring本身就提供了簡便的api,我們只需要獲取:

  • 方法:Method method
  • 方法引數:Object[] arguments
  • spel表示式:String spel

這些都能從aop入口方法的引數ProceedingJoinPoint中得到。

spel表示式顯然就是從自定義註解中獲取了,而獲取方法和引數的方式如下:

獲取方法:

private Method getMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                method = joinPoint
                        .getTarget()
                        .getClass()
                        .getDeclaredMethod(joinPoint.getSignature().getName(),
                                method.getParameterTypes());
            } catch (SecurityException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
        return method;
    }
複製程式碼

獲取方法引數值:

Object[] arguments = joinPoint.getArgs();
複製程式碼

解析

然後就是解析spel表示式,首先在aop類中定義兩個屬性:

private ExpressionParser parser = new SpelExpressionParser();

private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
複製程式碼

根據spel表示式解析引數,得到結果:

    /**
     * 解析 spel 表示式
     *
     * @param method    方法
     * @param arguments 引數
     * @param spel      表示式
     * @param clazz     返回結果的型別
     * @param defaultResult 預設結果
     * @return 執行spel表示式後的結果
     */
    private <T> T parseSpel(Method method, Object[] arguments, String spel, Class<T> clazz, T defaultResult) {
        String[] params = discoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], arguments[len]);
        }
        try {
            Expression expression = parser.parseExpression(spel);
            return expression.getValue(context, clazz);
        } catch (Exception e) {
            return defaultResult;
        }
    }
複製程式碼

總結

上述就是整個解析spel表示式的關鍵流程,整體來看,aop類的結構是這樣的:

@Aspect
public class SpelAspect {

    private ExpressionParser parser = new SpelExpressionParser();
    private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
    
    @Around(value = "@annotation(自定義註解)")
    public Object test(ProceedingJoinPoint point) throws Throwable {
        Object obj;
        // 獲取方法引數值
        Object[] arguments = point.getArgs();
        // 獲取方法
        Method method = getMethod(point);
        // 從註解中獲取spel字串,省略...
        String spel = ...
        // 解析spel表示式
        Boolean result = parseSpel(method, arguments, spel, Boolean.class, Boolean.FALSE);
        // 業務操作,省略...
        ...
        return point.proceed();
    }
}
複製程式碼

以上提供一個基本思路和幾個通用的方法(#getMethod#parseSpel),接下來就是大家發揮想象力的時間啦!

閱讀原文

相關文章