springboot2+spring cache+redis缓存

参考文章:
https://www.cnblogs.com/kingsonfu/p/10409596.html
https://www.cnblogs.com/wenjunwei/p/10779450.html
https://blog.csdn.net/zjcjava/article/details/103920388
redis中key含有两个冒号解决:
https://blog.csdn.net/chuixue24/article/details/103928965

tips:项目使用的springboot版本是2.2.5.RELEASE版本


1 spring cache

1.1 注解

详细内容可参考https://www.cnblogs.com/kingsonfu/p/10409596.html

注解作用
@Cacheable将方法的结果缓存起来,下一次方法执行参数相同时,将不执行方法,返回缓存中的结果
– value、cacheNames两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
– key缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
– condition缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存。
– unless另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
– keyGenerator用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
– cacheManager用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
– cacheResolver用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
@CacheEvict移除指定缓存
– allEntries非必需,默认为false。当为true时,会移除所有数据
– beforeInvocation非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
@CachePut标记该注解的方法总会执行,根据注解的配置将结果缓存
@Caching可以指定相同类型的多个缓存注解,例如根据不同的条件
@CacheConfig类级别注解,可以设置一些共通的配置,@CacheConfig(cacheNames=“user”), 代表该类下的方法均使用这个cacheNames

1.2 CacheManager缓存管理器

Spring针对不同的缓存技术,需要实现不同的CacheManager。本文实例使用的为RedisCacheManager

CacheManager描述
SimpleCacheManager使用简单的Collection来出循缓存,主要用于测试
ConcurrentMapCacheManager使用ConcurrentMap作为缓存技术(默认)
NoOpCacheManager测试用
EhCacheCacheManager使用EhCache作为缓存技术,以前在hibernate的时候经常用
GuavaCacheManager使用google GuavaCache作为缓存技术
HazelcastCacheManager使用Hazelcast作为缓存技术
JCacheCacheManager使用JCache标准的实现作为缓存技术,如Apache Commons JCS
RedisCacheManager使用redis作为缓存技术

2 实例

2.1 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.2 配置信息

spring:
  cache:
    # 缓存类型
    type: redis
    # 缓存失效时间
    ttl: 180
  redis:
    timeout: 10s
    lettuce:
      pool:
        max-active: 200
        max-idle: 8
        max-wait: 10s
        min-idle: 2
      shutdown-timeout: 3s
    database: 0
    port: 6379
    host: 127.0.0.1
    password: root

2.3 RedisCacheConfig

import com.sinosoft.springbootplus.mybatis.param.OrderQueryParam;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * <pre>
 * redis缓存
 * </pre>
 *
 * @author loopy_y
 * @since 2021/11/8
 */
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {

    @Resource
    private RedisConnectionFactory factory;

    /**
     * 默认缓存失效时间为60分钟
     */
    @Value("${spring.cache.ttl:60}")
    private long cacheTtl;

    /**
     * 自定义生成redis-key
     *
     * @return KeyGenerator
     */
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, objects) -> {
            // 缓存redis key 命名规则:  项目名<cacheName会自动拼接>:类名:MD5(方法名:参数)
            StringBuilder redisKey = new StringBuilder();
            // 拼接类名
            redisKey.append(o.getClass().getSimpleName());

            // 为避免redis key 太长,后续信息用MD5 加密
            StringBuilder keyStr = new StringBuilder();
            // 拼接方法名
            keyStr.append(method.getName());
            // 拼接参数
            for (Object obj : objects) {
                // 判断是否为分页查询接口 则参数包含  分页查询参数(页码-页大小):实体请求参数
                if (obj instanceof OrderQueryParam) {
                    Integer limit = ((OrderQueryParam) obj).getLimit();
                    Integer page = ((OrderQueryParam) obj).getPage();
                    keyStr.append(":").append(page).append("-").append(limit);
                }
                // 拼接实体请求参数
                keyStr.append(":").append(obj.toString());
            }
            // MD5加密 (方法名:参数)
            String md5Hex = DigestUtils.md5Hex(keyStr.toString());

            redisKey.append(":").append(md5Hex);
            return redisKey.toString();
        };
    }

    @Bean
    @Override
    public CacheManager cacheManager() {

        // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 设置缓存的默认过期时间,也是使用Duration设置
                .entryTtl(Duration.ofMinutes(cacheTtl))
                // 不缓存空值
                .disableCachingNullValues()
                // 覆盖默认的构造key,否则会多出一个冒号 原规则:cacheName::key  现规则:cacheName:key
                .computePrefixWith(name -> name + ":")
                // 设置序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }

}

2.4 service层添加缓存注解

@Slf4j
@Service
public class TestCacheService extends BaseServiceImpl<TestCacheMapper, TestCacheData> {

    // 在需要缓存的方法上添加缓存注解,因已使用自定义key,这里可不设置key
    @Cacheable(cacheNames = "cache")
    public List<TestCacheData> getSxServiceLocationDataList(String name) {
        QueryWrapper<TestCacheData> queryWrapper = Wrappers.<TestCacheData>query().eq("name", name);
        return getBaseMapper().selectList(queryWrapper);
    }
}

2.5 启动类上开启缓存@EnableCaching

// 启动类上添加@EnableCaching注解
@EnableCaching
@MapperScan({"com.test.cache.**.mapper"})
@SpringBootApplication
public class TestCacheApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(TestCacheApplication .class, args);
        // 打印项目信息
        PrintApplicationInfo.print(context);
    }
}

至此,配置缓存完成。

2.6 验证

第一次调用接口,可以看到方法执行,从数据库中查询出数据返回,且redis中生成了相应的key。相同方法相同参数再次调用,可看到并没有从数据库中重新查询出数据,是从缓存中获取直接返回的 --> 配置成功


3 常见问题汇总

3.1 redis中缓存的key有两个冒号

3.1.1 现象

可以看到,key值存在“::”的情况(图中2标示位置),使用redis desktop manager查看时会看到有一个空节点层(图中1标示位置)
redis中缓存的key有两个冒号

3.1.2 原因

redis的cache源码中,默认的key前缀生成策略后边就是拼接了两个冒号

package org.springframework.data.redis.cache;

@FunctionalInterface
public interface CacheKeyPrefix {
    String compute(String var1);

    static CacheKeyPrefix simple() {
        return (name) -> {
            return name + "::";
        };
    }
}

3.1.3 解决方式

RedisCacheConfig配置类CacheManager配置中设置:(可参考章节2.3 RedisCacheConfig)
 redis中缓存的key设置成一个冒号

3.2 redis缓存key过长

在进行自定义key设置时,为保证缓存key的唯一性,难免会使key的长度过长,可以将一部分key生成策略通过MD5加密的方式,来缩短key值。(可参考章节2.3 RedisCacheConfig)

3.3 redis缓存数据乱码序列化为json

RedisCacheConfig配置类CacheManager配置中设置:(可参考章节2.3 RedisCacheConfig)
redis缓存数据乱码序列化为json

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值