【Spring Boot学习笔记】集成 Redis

2025-06-03 02:00:49  阅读 61 次 评论 0 条

目前,Java 连接 Redis 分为两种客户端 一种是 Jedis,另一种则是 Lettuce

注:Spring Boot 2.0+ 使用 spring-boot-starter-data-redis 依赖,默认的客户端使用的是 Lettuce

1、Jedis 和 Lettuce 的区别

  • Jedis 在实现上,是直接连接的 Redis 服务器,在多个线程间共享一个 Jedis 实例是线程不安全的。如果是需要在多线程的场景下使用 Jedis ,需要使用连接池,让每个线程都使用自己的 Jedis 实例,当线程数量增多时,会消耗比较多的物理资源~

  • Jedis 相比的话,Lettuce 完全克服了前者线程不安全的缺点,多个线程可以共享一个实例,而不用担心多线程的并发问题~

2、Spring Boot 集成 Redis

1、添加相关依赖

 <!-- 集成redis依赖(默认使用lettuce)  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <!--通过排除 lettuce 依赖来实现 跳过它去用 Jedis,如果需要使用 Jedis,请解除注释下方配置 -->
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>io.lettuce</groupId>-->
<!--                    <artifactId>lettuce-core</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
        </dependency>

        <!-- 连接池所需的依赖 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--引入Jedis 依赖-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

2、添加数据源配置

我新建了一个 application-redis-test.yml 不清楚如何引用外部配置的,请移步 【Spring Boot学习笔记】配置文件的理解

#这个是Redis的配置文件
spring:
  data:
    redis:
      #Redis服务器地址
      host: 127.0.0.1
      #Redis服务端口
      port: 6379
      #Redis链接密码
      password: 114514
      #使用第几个数据库
      database: 0
      #链接超时时间
      timeout: 1000
      #这个是针对 lettuce客户端的配置
      lettuce:
        pool:
          #最大活跃连接
          max-active: 8
          #最大空闲连接(连接空闲时会保留在连接池中,后续可以直接复用)
          max-idle: 8
          #最小空闲连接
          min-idle: 0
          max-wait: 1000 # 连接等待时间,单位毫秒
      #这个是针对 jedis客户端的配置
      jedis:
        pool:
          #最大活跃连接
          max-active: 8
          #最大空闲连接(连接空闲时会保留在连接池中,后续可以直接复用)
          max-idle: 8
          #最小空闲连接
          min-idle: 0
          max-wait: 1000 # 连接等待时间,单位毫秒

3、定义Redis Config

这个是设定序列化与反序列化格式的~

/**
 *  这个类是设定key-value的序列化与反序列化
 *  告诉 Redis,我要把key序列化为String ,value序列化为Json
 */
@Configuration
public class RedisConfig {
    @Bean(name="redisTemplate")
    @SuppressWarnings("all")//暂时解决版本问题,导致报错 无法自动装配。找不到 'RedisConnectionFactory' 类型的 Bean。
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 设置key序列化方式string,RedisSerializer.string() 等价于 new StringRedisSerializer()
        redisTemplate.setKeySerializer(RedisSerializer.string());
        // 设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化,RedisSerializer.json() 等价于 new GenericJackson2JsonRedisSerializer()
        redisTemplate.setValueSerializer(RedisSerializer.json());
        // 设置hash的key的序列化方式
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置hash的value的序列化方式
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        // 使配置生效
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

4、创建 RedisUtil

这个根据自己需要,创建一个Redis统一的操作类,方便后续开发,这边我直接就用构造方法把Template注入了~

不到万不得已,别去用手动获取Bean!!!

@Component//非常重要,不然无法使用构造方法注入!,这个是告诉 Spring ,要以它管理的Bean加载到容器中~
public class RedisUtil {
    //private static RedisTemplate<String, Object> redisTemplate = SpringContextUtils.getBean("redisTemplate", RedisTemplate.class);
    private final RedisTemplate<String, Object> _redisTemplate;
    public RedisUtil(RedisTemplate<String, Object> redisTemplate) {
        _redisTemplate = redisTemplate;
    }


    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    public Boolean expire(String key, long time) {
        try {
            if (time > 0) {
                _redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回-1代表为永久有效 失效时间为0,说明该主键未设置失效时间(失效时间默认为-1)
     */
    public Long getExpire(String key) {
        return _redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false 不存在
     */
    public Boolean hasKey(String key) {
        try {
            return _redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                _redisTemplate.delete(key[0]);
            } else {
                var keys = (List<String>) CollectionUtils.arrayToList(key);
                _redisTemplate.delete(keys);
            }
        }
    }

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    @SuppressWarnings("unchecked")
    public  <T> T get(String key) {
        T res=key==null?null:(T)(_redisTemplate.opsForValue().get(key));
        //return key == null ? null : (T) _redisTemplate.opsForValue().get(key);
        return res;
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                _redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                _redisTemplate.opsForValue().set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


}

目前,前期的部署操作已经完成,下面创建测试方法~

5、初步体验

Service里面创建一个方法,可以根据自己的需求进行编写,我这边是如果在Redis中查到了这个用户的话,我把这个人的uid改为20000,如果没有查到的话,正常从数据库获取一遍,然后写入Redis,Key就是 KEY_USERINFO_xxx,这个 xxx 就是查询的用户名,大概像这样 (仅仅一个测试案例,别杠!)

private final RedisUtil _redisUtil;
public UserService(RedisUtil redisUtil) {
    _redisUtil=redisUtil;
}

//缓存实验
public UserInfo redisTest(String name){
    UserInfo ui=null;
    ui=_redisUtil.get("KEY_USERINFO_"+name);//获取数据
    if(ui==null) {


        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().like(UserInfo::getName, name).last("LIMIT 1");
        ui = userInfoMapper.selectOne(queryWrapper);
        if (ui != null) {
            _redisUtil.set("KEY_USERINFO_" + name, ui, (60 * 60 * 24));//缓存一天
        }
    }else{
        ui.setUid(20000);
    }
    return ui;
}

Controller那边我就不写了,删除就是 _redisUtil.del(key),当然多个key一起传也行~

测试效果:

QQ_1748887192812.png

刷新一下就变成了

QQ_1748887222335.png


完事~


微信扫码查看本文
本文地址:https://www.yangguangdream.com/?id=2252
版权声明:本文为原创文章,版权归 编辑君 所有,欢迎分享本文,转载请保留出处!

发表评论


表情

还没有留言,还不快点抢沙发?