查看原文
其他

[Springboot]SpringCache + Redis实现数据缓存

Rude3Knife 后端技术漫谈 2022-06-20

前言

本文实现了SpringCache + Redis的集中式缓存,方便大家对学习了解缓存的使用。

本文实现:

  • SpringCache + Redis的组合

  • 通过配置文件实现了自定义key过期时间;key命名方式;value序列化方式

实现本文代码的前提:

  • 已有一个可以运行的Springboot项目,实现了简单的CRUD功能

步骤

在Spring Boot中通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者:

  1. Generic

  2. JCache (JSR-107)

  3. EhCache 2.x

  4. Hazelcast

  5. Infinispan

  6. Redis

  7. Guava

  8. Simple

我们所需要做的就是实现一个将缓存数据放在Redis的缓存机制。

  • 添加pom.xml依赖

  1.    <!-- 缓存: spring cache -->

  2.    <dependency>

  3.        <groupId>org.springframework.boot</groupId>

  4.        <artifactId>spring-boot-starter-cache</artifactId>

  5.    </dependency>


  6.    <!-- 缓存: redis -->

  7.    <dependency>

  8.        <groupId>org.springframework.boot</groupId>

  9.        <artifactId>spring-boot-starter-data-redis</artifactId>

  10.    </dependency>

注意:- spring-boot-starter-data-redis和spring-boot-starter-redis的区别:https://blog.csdn.net/weixin_38521381/article/details/79397292

可以看出两个包并没有区别,但是当springBoot的版本为1.4.7 以上的时候,spring-boot-starter-redis 就空了。要想引入redis就只能选择有data的。


  • application.properties中加入redis连接设置(其它详细设置请查看参考网页)

  1. # Redis

  2. spring.redis.host=localhost

  3. spring.redis.port=6379

  4. spring.redis.pool.max-idle=8

  5. spring.redis.pool.min-idle=0

  6. spring.redis.pool.max-active=8

  7. spring.redis.pool.max-wait=-1

  8. spring.redis.database=0

  9. spring.redis.password=xxx


  • 新增KeyGeneratorCacheConfig.java(或者名为CacheConfig)文件

该文件完成三项设置:key过期时间;key命名方式;value序列化方式:JSON便于查看

  1. package com.pricemonitor.pm_backend;


  2. import org.springframework.beans.factory.annotation.Autowired;

  3. import org.springframework.cache.CacheManager;

  4. import org.springframework.cache.annotation.CachingConfigurerSupport;

  5. import org.springframework.cache.interceptor.KeyGenerator;

  6. import org.springframework.context.annotation.Configuration;

  7. import org.springframework.data.redis.cache.RedisCacheManager;

  8. import org.springframework.data.redis.core.RedisTemplate;

  9. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

  10. import org.springframework.data.redis.serializer.StringRedisSerializer;


  11. import java.lang.reflect.Method;


  12. @Configuration

  13. public class KeyGeneratorCacheConfig extends CachingConfigurerSupport {


  14.    private final RedisTemplate redisTemplate;


  15.    @Autowired

  16.    public KeyGeneratorCacheConfig(RedisTemplate redisTemplate) {

  17.        this.redisTemplate = redisTemplate;

  18.    }


  19.    @Override

  20.    public CacheManager cacheManager() {

  21.        // 设置key的序列化方式为String

  22.        redisTemplate.setKeySerializer(new StringRedisSerializer());

  23.        // 设置value的序列化方式为JSON

  24.        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

  25.        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);

  26.        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);

  27.        // 设置默认过期时间为600秒

  28.        cacheManager.setDefaultExpiration(600);

  29.        return cacheManager;

  30.    }


  31.    /**

  32.     * key值为className+methodName+参数值列表

  33.     * @return

  34.     */

  35.    @Override

  36.    public KeyGenerator keyGenerator() {

  37.        return new KeyGenerator() {

  38.            @Override

  39.            public Object generate(Object o, Method method, Object... args) {

  40.                StringBuilder sb = new StringBuilder();

  41.                sb.append(o.getClass().getName()).append("#");

  42.                sb.append(method.getName()).append("(");

  43.                for (Object obj : args) {

  44.                    if(obj != null) { // 在可选参数未给出时时,会出现null,此时需要跳过

  45.                        sb.append(obj.toString()).append(",");

  46.                    }

  47.                }

  48.                sb.append(")");

  49.                return sb.toString();

  50.            }

  51.        };

  52.    }

  53. }

  • 在serviceImpl中加入@CacheConfig并且给给每个方法加入缓存(详细注解使用请查看参考网页)

  1. @Service

  2. @CacheConfig(cacheNames = "constant")

  3. public class ConstantServiceImpl implements ConstantService {


  4.    @Autowired

  5.    private ConstantMapper constantMapper;


  6.    @Cacheable

  7.    @Override

  8.    public List<Constant> alertMessage() {

  9.        ConstantExample constantExample = new ConstantExample();

  10.        ConstantExample.Criteria criteria = constantExample.createCriteria();

  11.        criteria.andTypeEqualTo("alert");

  12.        return constantMapper.selectByExample(constantExample);

  13.    }


  14.    @Cacheable

  15.    @Override

  16.    public List<Constant> noteMessage() {

  17.        ConstantExample constantExample = new ConstantExample();

  18.        ConstantExample.Criteria criteria = constantExample.createCriteria();

  19.        criteria.andTypeEqualTo("note");

  20.        return constantMapper.selectByExample(constantExample);

  21.    }


  22.    @Cacheable

  23.    @Override

  24.    public List<Constant> banner() {

  25.        ConstantExample constantExample = new ConstantExample();

  26.        ConstantExample.Criteria criteria = constantExample.createCriteria();

  27.        criteria.andTypeEqualTo("banner");

  28.        return constantMapper.selectByExample(constantExample);

  29.    }

  30. }

效果图

注意事项


  • 若直接修改数据库的表,并没有提供接口修改的字段,缓存就没法更新。所以这种字段加缓存需要尤其注意缓存的有效性,最好让其及时过期。或者给其实现增删改接口。



  • 大坑:在可选参数未给出时时,会出现null,此时在生成Key名的时候需要跳过。已在代码中修改


  1. for (Object obj : args) {

  2.    if(obj != null) { // 在可选参数未给出时时,会出现null,此时需要跳过

  3.        sb.append(obj.toString()).append(",");

  4.    }

  5. }

参考

缓存入门:http://blog.didispace.com/springbootcache1/

Redis集中式缓存:http://blog.didispace.com/springbootcache2/

代码实现:(经典好用,有小bug):https://zhuanlan.zhihu.com/p/30540686

代码实现(可参考):https://www.jianshu.com/p/6ba2d2dbf36e


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存