栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

通过AOP+Java注解+EL表达式获取方法参数的值

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

通过AOP+Java注解+EL表达式获取方法参数的值

Java注解通过EL表达式获取方法参数值
    • 使用场景
    • 代码实现

使用场景

在使用AOP做切面时,每个方法的参数都不一致,有时候需要取某个参数值,由于AOP增强逻辑一般都是抽象通用封装的,不能指定写死取某个值,这时候可以使用注解+EL表达式来实现。

代码实现

以实现声明式分布式锁为例子

当方法需要加锁时,加上该注解。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface Lock {
    String value();

    String param() default "";

    long waitTime() default 30L;

    long leaseTime() default 100L;

    TimeUnit timeUnit() default TimeUnit.SECONDS;

    LockType type() default LockType.FAIR;
}

加锁示例

@RestController
@RequestMapping("/product")
@RequiredArgsConstructor
public class PackIndexController {

    private final UserEquitypackService userEquitypackService;

    @Lock(value = "test", param = "#product.name")
    @PostMapping("/test")
    public String test(Product product) {
            
    }
}

处理注解,解析EL表达式从product中获取值
先实现一个EL表达式解析器

public class ExpressionEvaluator extends CachedExpressionEvaluator {
    private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
    private final Map conditionCache = new ConcurrentHashMap<>(64);
    private final Map targetMethodCache = new ConcurrentHashMap<>(64);


    public EvaluationContext createEvaluationContext(Object object, Class targetClass, Method method, Object[] args) {
        Method targetMethod = getTargetMethod(targetClass, method);
        ExpressionRootObject root = new ExpressionRootObject(object, args);
        return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
    }


    public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class clazz) {
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
    }

    private Method getTargetMethod(Class targetClass, Method method) {
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.targetMethodCache.get(methodKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            if (targetMethod == null) {
                targetMethod = method;
            }
            this.targetMethodCache.put(methodKey, targetMethod);
        }
        return targetMethod;
    }
}

解析表达式需要的表达式根对象

public class ExpressionRootObject {
    private final Object object;
    private final Object[] args;

    public ExpressionRootObject(Object object, Object[] args) {
        this.object = object;
        this.args = args;
    }

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }
}

最后使用AspectJ的环绕通知去加锁及解析EL表达式获取值(为了便于理解,这些贴的关键代码,重点描述解析过程,而非实现分布式锁过程)

@Aspect
@Slf4j
public class RedisLockAspect  {
    private static final ExpressionEvaluator EVALUATOR = new ExpressionEvaluator<>();
    private final RedisLockClient redisLockClient;

    @Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, Lock redisLock) {
        log.info("======================into lock=======================");
        String lockName = redisLock.value();
        Assert.hasText(lockName, "@Lock value must have length; it must not be null or empty");
        String lockParam = redisLock.param();
        String lockKey;
        if (StringUtils.isNotBlank(lockParam)) {
        	// 解析EL表达式
            String evalAsText = this.evalLockParam(point, lockParam);
            lockKey = lockName + ':' + evalAsText;
        } else {
            lockKey = lockName;
        }

        LockType lockType = redisLock.type();
        long waitTime = redisLock.waitTime();
        long leaseTime = redisLock.leaseTime();
        TimeUnit timeUnit = redisLock.timeUnit();

        return this.redisLockClient.lock(lockKey, lockType, waitTime, leaseTime, timeUnit, point::proceed);
    }

    
    private String evalLockParam(ProceedingJoinPoint point, String lockParam) {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Object[] args = point.getArgs();
        Object target = point.getTarget();
        Class targetClass = target.getClass();
        EvaluationContext context = EVALUATOR.createEvaluationContext(target, target.getClass(), method, args);
        AnnotatedElementKey elementKey = new AnnotatedElementKey(method, targetClass);
        return EVALUATOR.condition(lockParam, elementKey, context, String.class);
    }

    public RedisLockAspect(final RedisLockClient redisLockClient) {
        this.redisLockClient = redisLockClient;
    }
}

用postman调/product/test这个接口,传递参数为

{
	name:"小米手机"
}

此时由于加了@lock注解,所以会被RedisLockAspect拦截处理
代码中最终lockKey 的值为:
test:小米手机

自此就成功通过AOP+Java注解+EL表达式获取了方法参数的值.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983962.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号