gpt4 book ai didi

java - 使用Spring SpEL表达式获取Annotation中引用的动态参数

转载 作者:行者123 更新时间:2023-12-02 12:57:52 28 4
gpt4 key购买 nike

我想做的是拥有一个看起来很像Spring提供的@Cacheable Annotation的注解。

在方法之上使用,如下所示:

@CleverCache(key = "'orders_'.concat(#id)")
public Order getOrder(int id) {

当我使用相同的 Cacheable 时,它​​能够以某种方式解释此 SpEL 表达式并生成一个值为 orders_1234 (对于 id=1234)的 key

我的匹配建议如下所示:

@Around("CleverCachePointcut(cleverCache)")
public Object clevercache(ProceedingJoinPoint joinPoint, CleverCache cleverCache) throws Throwable {
String expression = cleverCache.key();
//FIXME: Please add working code here :D - extracting the key by interpreting the passed SpEL Expression in expression

我确实得到了那里的表达式,但我还没有弄清楚如何让它正确解释 SpEL 表达式。

另一个支持语法应该是key = "T(com.example.Utils).createCacheKey(#paramOfMethodByName)",其中调用用于创建 key 的静态帮助器。

知道这是如何运作的吗?我从中获取片段的代码位于: https://github.com/eiselems/spring-redis-two-layer-cache/blob/master/src/main/java/com/marcuseisele/example/twolayercache/clevercache/ExampleAspect.java#L35

非常感谢任何帮助!

最佳答案

如果您有必要的上下文信息,评估 SpEL 实际上非常简单。请引用this article以了解如何以编程方式解析 SpEL。

至于上下文信息,您没有对 @CleverCache 注释的方法类型进行太多解释。问题是,切入点拦截所有带注释的方法,并且我不知道每个方法的第一个参数是否是 int ID。根据这个问题的答案,从被拦截的方法中获取 ID 参数值会更容易(只是一种简单的情况)或更困难(您需要 if-else 才能找到具有整数 ID 的方法)。或者,也许您有各种引用方法参数、实例变量或其他内容的多种类型和名称的表达式。解决方案的复杂性与需求的复杂性相关。如果您提供更多信息,也许我也可以提供更多帮助。

<小时/>

更新:查看了您的 GitHub 存储库后,我针对简单案例重构了您的方面:

package com.marcuseisele.example.twolayercache.clevercache;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class ExampleAspect {
private static final ExpressionParser expressionParser = new SpelExpressionParser();

private Map<String, RedisTemplate> templates;

public ExampleAspect(Map<String, RedisTemplate> redisTemplateMap) {
this.templates = redisTemplateMap;
}

@Pointcut("@annotation(cleverCache)")
public void CleverCachePointcut(CleverCache cleverCache) {
}

@Around("CleverCachePointcut(cleverCache) && args(id)")
public Object clevercache(ProceedingJoinPoint joinPoint, CleverCache cleverCache, int id) throws Throwable {
long ttl = cleverCache.ttl();
long grace = cleverCache.graceTtl();

String key = cleverCache.key();
Expression expression = expressionParser.parseExpression(key);
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("id", id);
String cacheKey = (String) expression.getValue(context);
System.out.println("### Cache key: " + cacheKey);

long start = System.currentTimeMillis();
RedisTemplate redisTemplate = templates.get(cleverCache.redisTemplate());
Object result;
if (redisTemplate.hasKey(cacheKey)) {
result = redisTemplate.opsForValue().get(cacheKey);
log.info("Reading from cache ..." + result.toString());

if (redisTemplate.getExpire(cacheKey, TimeUnit.MINUTES) < grace) {
log.info("Entry is in Grace period - trying to refresh it");
try {
result = joinPoint.proceed();
redisTemplate.opsForValue().set(cacheKey, result, grace+ttl, TimeUnit.MINUTES);
log.info("Fetch was successful - new value will be returned");
} catch (Exception e) {
log.warn("An error occured while trying to refresh the value - extending the old one", e);
//TODO: think about only adding 5 minutes on top of grace, or 50% of ttl on top of grace
//if protected by a circuit breaker we could go REALLY low here
redisTemplate.opsForValue().getOperations().expire(cacheKey, grace+ttl, TimeUnit.MINUTES);
}

}

} else {
result = joinPoint.proceed();
log.info("Giving from method ..." + result.toString());
redisTemplate.opsForValue().set(cacheKey, result, ttl + grace, TimeUnit.MINUTES);
}

long executionTime = System.currentTimeMillis() - start;
log.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);
log.info("Result: {}", result);
return result;
}
}

差异如下所示:

Diff screenshot

关于java - 使用Spring SpEL表达式获取Annotation中引用的动态参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53822544/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com