51

Spring Aop中解析spel表达式,实现更灵活的功能

 5 years ago
source link: http://www.ciphermagic.cn/spring-aop-spel.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在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
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 表达式
*
*@parammethod 方法
*@paramarguments 参数
*@paramspel 表达式
*@paramclazz 返回结果的类型
*@paramdefaultResult 默认结果
*@return执行spel表达式后的结果
*/
private <T> TparseSpel(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 ),接下来就是大家发挥想象力的时间啦!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK