Spring-Cache key设置注意事项,及自定义keyGenerator

本文深入探讨了如何在项目中使用Spring Cache和Guava Cache来提高并发性能,通过实例详细介绍了不同场景下缓存key的配置方法,包括基本形式、组合形式、对象形式以及自定义Key生成器,解决了多参数、原子类型数组、方法名识别等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了提升项目的并发性能,考虑引入本地内存Cache,对:外部数据源访问、Restful API调用、可重用的复杂计算 等3种类型的函数处理结果进行缓存。目前采用的是Spring Cache的@Cacheable注解方式,缓存具体实现选取的是Guava Cache。

具体缓存的配置此处不再介绍,重点对于key的配置进行说明:

1、基本形式

@Cacheable(value="cacheName", key"#id")
public ResultDTO method(int id);

2、组合形式

@Cacheable(value="cacheName", key"T(String).valueOf(#name).concat('-').concat(#password))
public ResultDTO method(int name, String password);

3、对象形式

@Cacheable(value="cacheName", key"#user.id)
public ResultDTO method(User user);

4、自定义Key生成器

@Cacheable(value="gomeo2oCache", keyGenerator = "keyGenerator")
public ResultDTO method(User user);

有一个尤其需要注意的坑:Spring默认的SimpleKeyGenerator是不会将函数名组合进key中的

举个栗子:

@Component  
    public class CacheTestImpl implements CacheTest {
    
        @Cacheable("databaseCache")
        public Long test1()
        { return 1L; }
        
        @Cacheable("databaseCache")

        public Long test2()

        { return 2L; }

        @Cacheable("databaseCache")

        public Long test3()

        { return 3L; }

        @Cacheable("databaseCache")

        public String test4()

        { return "4"; }

    }

我们期望的输出是:

1
2
3
4

而实际上的输出是:

1
1
1
ClassCastException: java.lang.Long cannot be cast to java.lang.String

此外,原子类型的数组,直接作为key使用也是不会生效的

为了解决上述2个问题,自定义了一个KeyGenerator如下:

class CacheKeyGenerator implements KeyGenerator {

	// custom cache key
	
    public static final int NO_PARAM_KEY = 0;

    public static final int NULL_PARAM_KEY = 53;

    @Override

    public Object generate(Object target, Method method, Object... params) {

        StringBuilder key = new StringBuilder();
        key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":");
        if (params.length == 0) {
			return key.append(NO_PARAM_KEY).toString();
		   }



        for (Object param : params) {



            if (param == null) {



                log.warn("input null param for Spring cache, use default key={}", NULL_PARAM_KEY);



                key.append(NULL_PARAM_KEY);



            } else if (ClassUtils.isPrimitiveArray(param.getClass())) {



                int length = Array.getLength(param);



                for (int i = 0; i < length; i++) {



                    key.append(Array.get(param, i));



                    key.append(',');



                }



            } else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {



                key.append(param);



            } else {



                log.warn("Using an object as a cache key may lead to unexpected results. " +



                        "Either use @Cacheable(key=..) or implement CacheKey. Method is " + target.getClass() + "#" + method.getName());



                key.append(param.hashCode());



            }



            key.append('-');



        }



 



        String finalKey = key.toString();



        long cacheKeyHash = Hashing.murmur3_128().hashString(finalKey, Charset.defaultCharset()).asLong();



        log.debug("using cache key={} hashCode={}", finalKey, cacheKeyHash);



        return key.toString();



    }



}

采用此方式后可以解决:多参数、原子类型数组、方法名识别 等问题

https://blog.csdn.net/syani/article/details/52239967

<think>好的,我现在要回答用户关于Spring Cachekey生成规则及自定义配置方法的问题。首先,我需要回顾一下Spring Cache的基本概念和相关注解,尤其是@Cacheable、@CachePut和@CacheEvict这些常用注解。根据用户提供的引用内容,我需要整合这些信息,确保回答准确。 用户提到他们想了解key的用法和配置问题,所以我需要先解释默认的key生成规则,然后再说如何自定义。根据引用[1],默认的Key生成策略是使用SimpleKeyGenerator,它基于方法参数生成key。如果有多个参数,会生成包含所有参数的键;如果无参数,则返回SimpleKey.EMPTY。这部分需要详细说明,让用户明白默认行为是怎样的。 接下来是自定义key的方法,这需要使用SpEL表达式。引用[2]、[3]、[4]都提到了在@Cacheable注解中使用key属性,并给出了一些例子,比如key = "#root.method.name"或者组合多个参数。这里需要举几个例子,说明不同的SpEL表达式如何影响key的生成,比如使用方法名、参数属性等。 然后是关于自定义KeyGenerator的部分。引用[1]和[5]提到可以自定义实现KeyGenerator接口,并在配置类中声明Bean。需要说明实现步骤,例如创建一个类实现KeyGenerator接口,重写generate方法,然后在注解中指定generator属性。同时,用户需要注意如果配置了全局的key-prefix,可能会导致value失效,如引用[4]所提到的,这点需要特别提醒用户,避免配置错误。 另外,引用[5]提到缓存到Redis的key是value和key值的拼接,这里可能需要举例说明生成的key格式,比如user::getUserById_123,这样用户能更直观地理解拼接方式。 在组织回答结构时,应该先分点说明默认规则,再讲自定义方法,再提配置注意事项。同时,确保引用标识正确对应,比如引用[1]对应默认生成器,引用[4]对应key-prefix的问题等。最后生成相关问题,需要围绕Spring Cache的其他配置、SpEL的用法、性能优化等方面提问,帮助用户扩展知识。 需要检查是否有遗漏的信息,例如是否提到缓存分区的value属性以及key-prefix的影响,确保覆盖用户可能遇到的问题。另外,注意使用中文,避免技术术语错误,保持回答清晰易懂。</think>### Spring CacheKey生成规则及自定义配置方法 #### 一、默认Key生成规则 Spring Cache默认使用`SimpleKeyGenerator`生成缓存Key,规则如下: 1. **无参数方法**:Key为`SimpleKey.EMPTY`,例如:`value::SimpleKey.EMPTY`[^1]。 2. **单个参数方法**:直接使用参数值作为Key,例如参数为`123`时生成`value::123`。 3. **多个参数方法**:将所有参数组合成`SimpleKey`对象,例如参数为`(1, "test")`时生成`value::1-test`[^4]。 #### 二、自定义Key的3种方式 1. **通过SpEL表达式定义Key** 在`@Cacheable`注解中直接指定`key`属性,支持Spring表达式语言(SpEL): ```java @Cacheable(value="user", key="#root.methodName + '_' + #id") // 示例:getUserById_123 public User getUserById(Long id) { ... } @Cacheable(value="order", key="#user.id + ':' + #orderType") // 示例:10001:VIP public Order getOrder(User user, String orderType) { ... } ``` 常用SpEL变量: - `#root.methodName`:当前方法名 - `#root.targetClass`:目标类 - `#参数名`:方法参数值[^4] 2. **自定义KeyGenerator实现类** 步骤: ```java // 1. 实现KeyGenerator接口 public class CustomKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { return method.getName() + "_" + Arrays.hashCode(params); } } // 2. 声明Bean @Configuration public class CacheConfig { @Bean public KeyGenerator customKeyGenerator() { return new CustomKeyGenerator(); } } // 3. 使用注解指定 @Cacheable(value="product", keyGenerator="customKeyGenerator") ``` 此方式适合需要全局统一Key生成逻辑的场景[^1][^5]。 3. **配置全局Key前缀** 在`application.properties`中设置: ```properties spring.cache.redis.key-prefix=CACHE_ spring.cache.redis.use-key-prefix=true ``` **注意**:启用全局前缀后,`@Cacheable`的`value`将失效,所有Key以全局前缀开头[^4]。 #### 三、配置注意事项 | 场景 | 解决方案 | |-----------------------|--------------------------------------------------------------------------| | 参数包含复杂对象 | 使用`#参数名.属性`,如`#user.id` | | 需要拼接多级Key | 组合SpEL表达式,如`key="#type + ':' + #id"` | | 全局前缀与局部Key冲突 | 禁用`use-key-prefix`,在SpEL中手动拼接前缀 | | 热Key问题 | 在Key生成规则中加入随机因子(如时间戳) | #### 四、示例:Redis中的最终Key格式 若配置全局前缀`CACHE_`且使用`@Cacheable(value="user", key="#id")`,实际存储的Key为: ``` CACHE_user::123 ``` 其中`user`为缓存分区,`::`是Spring默认的分隔符。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值