buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

spring @Cacheable使用SpEL异常:org.springframework.expression.spel.SpelParseException: Unexpected token. Expected 'identifier' but was 'lcurly({)'

在 Spring 中,@Cacheable 注解是用于启用方法结果的缓存功能。

springboot结合redis做缓存,在@Cacheable中使用如下SpEL时报错。

@Cacheable(cacheNames = ENTERPRISE_CACHE_KEY,
key = "#{T(com.emax.common.RestApiSignUtil).foo(#root.args[0])}")

异常:

org.springframework.expression.spel.SpelParseException: Expression [#{T(com.emax.common.RestApiSignUtil).foo(#root.args[0])}] @1: EL1043E: Unexpected token. Expected 'identifier' but was 'lcurly({)'

异常stacktrace信息:

org.springframework.expression.spel.SpelParseException: Expression [#{T(com.emax.common.RestApiSignUtil).foo(#root.args[0])}] @1: EL1043E: Unexpected token. Expected 'identifier' but was 'lcurly({)'

    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.internalException(InternalSpelExpressionParser.java:1044)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatToken(InternalSpelExpressionParser.java:926)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.maybeEatFunctionOrVar(InternalSpelExpressionParser.java:423)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatStartNode(InternalSpelExpressionParser.java:512)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatPrimaryExpression(InternalSpelExpressionParser.java:351)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatUnaryExpression(InternalSpelExpressionParser.java:345)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatPowerIncDecExpression(InternalSpelExpressionParser.java:304)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatProductExpression(InternalSpelExpressionParser.java:282)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatSumExpression(InternalSpelExpressionParser.java:264)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatRelationalExpression(InternalSpelExpressionParser.java:218)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatLogicalAndExpression(InternalSpelExpressionParser.java:205)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatLogicalOrExpression(InternalSpelExpressionParser.java:192)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatExpression(InternalSpelExpressionParser.java:153)
    at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:131)
    at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:61)
    at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:33)
    at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:52)
    at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:43)
    at org.springframework.context.expression.CachedExpressionEvaluator.getExpression(CachedExpressionEvaluator.java:88)
    at org.springframework.cache.interceptor.CacheOperationExpressionEvaluator.key(CacheOperationExpressionEvaluator.java:104)
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:778)
    at org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:575)
    at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:518)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:401)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
    at com.emax.MyService$$EnhancerBySpringCGLIB$$4de73e25.getEnterpriseByEntId(<generated>)
    at com.emax.MyServiceTest.getEnterpriseByEntId(MyServiceTest.java:21)
View Code

 

identifier、lcurly都是什么?一脸懵逼。

百度翻译: curly 英/ˈkɜːli/ 美/ˈkɜːrli/ adj. 卷曲的;有鬈发(或毛)的;拳曲状的 比较级:curlier,最高级:curliest 
顺着stacktrace点进去看源码,才确定lcurly表示左大括号“{”。

后来在百度翻译里才发现,括号是 braces; curly braces是花括号,即"{}"。

看来不能用“{”。下面是正确答案:

@Cacheable(cacheNames = ENTERPRISE_CACHE_KEY, 
key = "T(com.emax.common.RestApiSignUtil).foo(#root.args[0])")

 

在SpEL中可以调用类的方法,足见SpEL的强大!


扩展:@Cacheable中SpEL的用法列举

在 Spring 中,@Cacheable 注解是用于启用方法结果的缓存功能。它允许我们定义缓存的键(key)和条件(condition)等。SpEL(Spring Expression Language)可以在 @Cacheable 注解中使用,以动态地设置缓存的键和条件。

// 下面方式可以同时设置两个缓存; 取参数可以用#param形式

@Cacheable(cacheNames = {ENTERPRISE_CACHE_KEY, "test-another-key"}, key = "#enterpriseId eq 0 ? 0: #enterpriseId")
public Result baseEnterpriseByEntId(Long enterpriseId) {...}

-------------------------------------------------

// #root代表的是spring-context-***.jar中的CacheExpressionRootObject,该类里有Object[] args属性及其getter方法。

@Cacheable(cacheNames = ENTERPRISE_CACHE_KEY, key = "#root.args[0]<=0? 0 : #root.args[0]")
public Result getEnterpriseByEntId1(Long enterpriseId) {...}

//使用对象属性作为缓存键

@Cacheable(value = "myCache", key = "#user.id")
public User getUserData(User user) {
    // ...
    return result;
}

-------------------------------------------------

// SpEL中调用外部类函数

@Cacheable(cacheNames = ENTERPRISE_CACHE_KEY, key = "T(com.emax.common.MyUtil).foo(#root.args[0])")
public Result getEnterpriseByEntId(Long enterpriseId) {...}

// SpEL中调用外部类函数--- 设置缓存key常用的套路:当缓存key包含一个对象时,则做md5减少key串长度

@Cacheable(cacheNames = ENTERPRISE_CACHE_KEY, key = "T(com.emax.common.Md5Util).sign(T(com.alibaba.fastjson.JSON).toJSONString(#root.args[0]))")
public Result getEnterpriseByEntId2(UserAccountVo userAccountVo, Long enterpriseId) {...}

 

// 使用多个方法参数作为缓存键

@Cacheable(value = "myCache", key = "#param1 + '_' + #param2")
public String getCachedData(String param1, int param2) {
    // ...
    return result;
}

 

// 使用条件进行缓存控制

下述示例中,#result != null 表达式表示只有当方法返回结果不为 null 时才进行缓存。

@Cacheable(value = "myCache", condition = "#result != null")
public String getCachedData() {
    // ...
    return result;
}

 

posted on 2022-10-11 21:08  buguge  阅读(3495)  评论(0)    收藏  举报