Разбор орфографических выражений в Spring Aop для достижения более гибких функций

Spring
Разбор орфографических выражений в Spring Aop для достижения более гибких функций

предисловие

В 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) {

В этой статье рассказывается, как анализировать spel-выражения в АОП-программировании, и приводятся несколько общих методов.

То, как Spring использует пользовательские аннотации для реализации aop, здесь не будет повторяться, а будет сосредоточено только на том, как анализировать spel.

Подготовить

Реализация очень простая, Spring сам предоставляет простой API, нам нужно только получить:

  • метод:Method method
  • Параметры метода:Object[] arguments
  • орфографическое выражение: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), то всем пора фантазировать!

читать оригинал