SpringBoot18 redis的配置方法

2025-11-20 17:50

本文主要是介绍SpringBoot18 redis的配置方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot18redis的配置方法》本文介绍在SpringBoot项目中集成和使用Redis的方法,包括添加依赖、配置文件、自定义序列化方式、使用方式、实际使用示例、常见操作总结以及注意...

一、Spring Boot 中使用 Redis

我来详细介绍如何在 Spring Boot 项目中集成和使用 Redis。

1. 添加依赖

pom.XML 中添加 Spring Data Redis 依赖:

<dependencies>
    <!-- Spring Boot Redis 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- Lettuce 连接池(可选,推荐) -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- 如果需要使用 JSON 序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

2. 配置文件

application.ymlapplication.properties 中配置 Redis:

application.yml:

spring:
  redis:
    host: localhost
    port: 6379
    password: # 如果有密码就填写
    database: 0 # Redis 数据库索引(默认为0)
    timeout: 3000ms # 连接超时时间
    lettuce:
 php     pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8 # 连接池最大空闲连接数
        min-idle: 0 # 连接池最小空闲连接数
        max-wait: -1ms # 连接池最大阻塞等待时间

3. Redis 配置类(推荐)

创建配置类以自定义序列化方式:

package com.example.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        // 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 value
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 使用 StringRedisSerializer 来序列化和反序列化 key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key 采用 String 的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash 的 key 也采用 String 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 序列化方式采用 jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的 value 序列化方式采用 jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4. 使用方式

方式一:直接注入 RedisTemplate

package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import Java.util.concurrent.TimeUnit;
@Service
public class UserService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // 设置值
    public void setUser(String key, User user) {
        redisTemplate.opsForValue().set(key, user);
    }
    // 设置值并设置过期时间
    public void setUserWithExpire(String key, User user, long timeout) {
        redisTemplate.opsForValue().set(key, user, timeout, TimeUnit.SECONDS);
    }
    // 获取值
    public User getUser(String key) {
        return (User) redisTemplate.opsForValue().get(key);
    }
    // 删除
    public void deleteUser(String key) {
        redisTemplate.delete(key);
    }
    // 判断 key 是否存在
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
    // 设置过期时间
    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
}

方式二:封装 Redis 工具类(推荐)

package com.example.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // ========== String 操作 ==========
    /**
     * 设置缓存
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 设置缓存并设置过期时间
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }
    /**
     * 获取缓存
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 删除缓存
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    /**
     * 批量删除
     */
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }
    /**
     * 设置过期时间
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 判断 key 是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 递增
     */
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    /**
     * 递减
     */
    public Long decrement(String key, long delta) {
        return redisTemplate.opsForValue().decrement(key, delta);
    }
    // ========== Hash 操作 ==========
    /**
     * Hash 设置
     */
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    /**
     * Hash 获取
     */
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    /**
     * Hash 删除
     */
    public Long hDelete(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }
    // ========== List 操作 ==========
    /**
     * List 右侧推入
     */
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }
    /**
     * List 左侧弹出
     */
    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    // ========== Set 操作 ==========
    /**
     * Set 添加
     */
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    /**
     * Set 移除
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }
    // ========== Sorted Set 操作 ==========
    /**
     * ZSet 添加
     */
    public Boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZjsSet().add(key, value, score);
    }
}

5. 实际使用示例

@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private RedisUtil redisUtil;
    // 缓存用户信息
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        String key = "user:" + id;
        // 先从缓存中获取
        User user = (User) redisUtil.get(key);
        if (user == null) {
            // 缓存未命中,从数据库查询
            user = userService.getUserById(id);
            if (user != null) {
                // 存入缓存,过期时间 30 分钟
                redisUtil.set(key, user, 30, TimeUnit.MINUTES);
            }
        }
        return user;
    }
    // 更新用户时删除缓存
    @PutMapping("/{id}")
    public void updateUser(@PathVariable Long id, @RequestBody User user) {
        userService.updateUser(user);
        // 删除缓存
        redisUtil.delete("user:" + id);
    }
}

6. 常见操作总结

数据类型RedisTemplate 方法说明
StringopsForValue()字符串操作
HashopsForHash()哈希表操作
ListopsForList()列表操作
SetopsForSet()集合操作
Sorted SetopsForZSet()有序集合操作

7. 注意事项

  1. 序列化问题:默认使用 JDK 序列化,建议使用 JSON 序列化(如上面配置类所示)
  2. 缓存穿透:查询不存在的数据,建议缓存空值或使用布隆过滤器
  3. 缓存雪崩:大量缓存同时失效,建议设置随机过期时间
  4. 缓存击穿:热点数据过期,建议使用互斥锁或永不过期
  5. 连接池配置:根据实际并发量调整连接池参数

二、为什么要编写redis配置类呢

1. 可以直接使用,但有问题

引入依赖后可以直接 @Autowired 注入使用,Spring Boot 会自动配置一个默认的 RedisTemplate。

@Autowired
private RedisTemplate redisTemplate; // 可以直接用

但是,默认的 RedisTemplate 有一个严重问题:序列化方式!

2. 默认序列化的问题

Spring Boot 默认的 RedisTemplate 使用 JDK 序列化(JdkSerializationRedisSerializer),会导致:

问题演示:

// 存入数据
redisTemplate.opsForValue().set("user:1", user);

在 Redis 中看到的数据是这样的:

Key: "\xac\xed\x00\x05t\x00\x06user:1"
Value: "\xac\xed\x00\x05sr\x00\x1ccom.example.entity.User..."

问题有哪些?

  1. 不可读:存储的是二进制数据,无法直接在 Redis 客户端查看
  2. 占用空间大:JDK 序列化后的数据比 JSON 大很多
  3. 跨语言不兼容:其他语言(如 python、Go)无法读取 Java 序列化的数据
  4. 安全风险:JDK 序列化存在已知的安全漏洞

对比:使用 JSON 序列化

Key: "user:1"
Value: {"id":1,"name":"张三","age":25}

这样就:

  • ✅ 可读性强
  • ✅ 体积更小
  • ✅ 跨语言兼容
  • ✅ 更安全

3. 为什么是 RedisTempChina编程late<String, Object>?

泛型说明

RedisTemplate<K, V>
// K: Key 的类型
// V: Value 的类型

为什么用<String, Object>?

Key 使用 String:

// Redis 的 编程key 通常都是字符串
"user:1"
"product:100"
"cache:article:20"

Value 使用 Object:

// 可以存储各种类型的对象
redisTemplate.opsForValue().set("user:1", userObject);      // User 对象
redisTemplate.opsForValue().set("count", 100);              // Integer
redisTemplate.opsForValue().set("list", Arrays.asList(1,2,3)); // List

使用 Object 类型最灵活,可以存储任何对象。

4. 配置类做了什么?

让我简化说明配置类的作用:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    // 关键:修改序列化方式
    // 1. Key 使用 String 序列化
    template.setKeySerializer(new StringRedisSerializer());
    // 2. Value 使用 JSON 序列化
    template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
    // 3. Hash 的 Key 和 Value 也设置序列化方式
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
    return template;
}

核心作用就是:把默认的 JDK 序列化改成 JSON 序列化!

5. 不配置 vs 配置的对比

场景:存储用户对象

User user = new User(1L, "张三", 25);
redisTemplate.opsForValue().set("user:1", user);

不配置(使用默认)

Redis 中存储的内容:

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x06user:1"
127.0.0.1:6379> get "\xac\xed\x00\x05t\x00\x06user:1"
"\xac\xed\x00\x05sr\x00\x1c..." # 一堆乱码

配置后(使用 JSON)

Redis 中存储的内容:

127.0.0.1:6379> keys *
1) "user:1"
127.0.0.1:6379> get user:1
"{\"id\":1,\"name\":\"张三\",\"age\":25}"

是不是清晰多了?

总结

  1. 可以不配置,但会用 JDK 序列化,导致数据不可读
  2. 配置的目的:改用 JSON 序列化,让数据可读、节省空间、跨语言兼容
  3. <String, Object>:Key 用字符串,Value 用 Object 更灵活
  4. 推荐做法:写配置类,统一使用 JSON 序列化

三、配置类中的ObjectMapper的相关操作

redis配置类中有一段代码:

ObjectMapper objectMapper = new ObjectMapper(); 
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 
objectMapper.activateDefaultTyping( LaissezFaireSubTypeValidator.instance, 
ObjectMapper.DefaultTyping.NON_FINAL ); 
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

让我逐行解释这段代码的作用。

这段代码在解决什么问题?

核心问题:反序列化时的类型丢失

当你从 Redis 取出数据时,Jackson 不知道原始对象是什么类型,只能反序列化成 LinkedHashMap 或其他通用类型。

问题演示

// 存入 User 对象
User user = new User(1L, "张三", 25);
redisTemplate.opsForValue().set("user:1", user);
// 取出时
Object obj = redisTemplate.opsForValue().get("user:1");
System.out.println(obj.getClass()); 
// 输出:class java.util.LinkedHashMap(不是 User!)
// 无法直接使用
User user = (User) obj; // 报错:ClassCastException

逐行解释

1. 创建 ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();

这是 Jackson 的核心类,负责 Java 对象和 JSON 之间的转换。

2. 设置可见性

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

作用:告诉 Jackson 可以访问对象的所有属性

PropertyAccessor.ALL 包括:

  • FIELD(字段)
  • GETTER(get 方法)
  • SETTER(set 方法)
  • CREATOR(构造方法)
  • IS_GETTER(is 方法)

JsonAutoDetect.Visibility.ANY 表示:

  • public 可以访问
  • protected 可以访问
  • private 也可以访问 ⬅️ 关键
示例:
public class User {
    private Long id;        // private 字段
    private String name;    // private 字段
    // 没有 getter/setter 也能序列化!
}

不设置这个配置的话:

// 默认只能访问 public 字段或有 getter/setter 的字段
// private 字段没有 getter 就无法序列化

3. 激活默认类型信息(重点!)

objectMapper.activateDefaultTyping(
    LaissezFaireSubTypeValidator.instance,
    ObjectMapper.DefaultTyping.NON_FINAL
);

这是最关键的配置!作用:在 JSON 中存储类型信息

不配置时的问题:

存入 Redis:

{
  "id": 1,
  "name": "张三",
  "age": 25
}

从 Redis 取出:

Object obj = redisTemplate.opsForValue().get("user:1");
// obj 是 LinkedHashMap,不是 User!
// 因为 Jackson 不知道原始类型是什么
配置后的效果:

存入 Redis(包含类型信息):

[
  "com.example.entity.User",
  {
    "id": 1,
    "name": "张三",
    "age": 25
  }
]

从 Redis 取出:

Object obj = redisTemplate.opsForValue().get("user:1");
// obj 就是 User 类型!可以直接转换
User user = (User) obj; // ✅ 成功
参数说明:

LaissezFaireSubTypeValidator.instance

  • 一个宽松的类型验证器
  • 允许反序列化几乎所有类型
  • LaissezFaire 是法语,意思是"放任自由"

ObjectMapper.DefaultTyping.NON_FINAL

  • 非 final 类添加类型信息
  • 选项包括:
    • JAVA_LANG_OBJECT:只对 Object 类型
    • OBJECT_AND_NON_CONCRETE:Object 和抽象类/接口
    • NON_CONCRETE_AND_ARRAYS:抽象类、接口和数组
    • NON_FINAL:所有非 final 类 ⬅️ 最常用
    • EVERYTHING:所有类型

4. 设置到序列化器

jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

把配置好的 ObjectMapper 设置给 Jackson 序列化器,让它使用我们的配置。

完整效果对比

配置前(类型丢失)

// 存入
User user = new User(1L, "张三", 25);
redisTemplate.opsForValue().set("user:1", user);
// Redis 中存储:
{
  "id": 1,
  "name": "张三",
  "age": 25
}
// 取出
Object obj = redisTemplate.opsForValue().get("user:1");
System.out.println(obj.getClass());
// 输出:class java.util.LinkedHashMap ❌
User user = (User) obj; // 报错!ClassCastException

配置后(保留类型)

// 存入
User user = new User(1L, "张三", 25);
redisTemplate.opsForValue().set("user:1", user);
// Redis 中存储:
[
  "com.example.entity.User",
  {
    "id": 1,
    "name": "张三",
    "age": 25
  }
]
// 取出
Object obj = redisTemplate.opsForValue().get("user:1");
System.out.println(obj.getClass());
// 输出:class com.example.entity.User ✅
User user = (User) obj; // 成功!

代码简化版(帮助理解)

ObjectMapper objectMapper = new ObjectMapper();
// 1. 让 Jackson 能访问 private 字段
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 2. 在 JSON 中保存类型信息(最重要!)
objectMapper.activateDefaultTyping(
    LaissezFaireSubTypeValidator.instance,  // 验证器:允许所有类型
    ObjectMapper.DefaultTyping.NON_FINAL    // 为非 final 类添加类型信息
);
// 3. 应用这些配置
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

注意事项

1. 安全风险

// 使用 LaissezFaireSubTypeValidator 可能有安全风险
// 因为它允许反序列化任何类型
// 生产环境可以考虑更严格的验证器

2. 性能影响

类型信息会增加存储空间
// 原始:{"id":1,"name":"张三"}
// 带类型:["com.example.User",{"id":1,"name":"张三"}]

3. 替代方案

如果不想使用类型信息,可以手动指定类型:

// 存入时就明确类型
ValueOperations<String, User> ops = redisTemplate.opsForValue();
ops.set("user:1", user);
// 取出时也明确类型
User user = ops.get("user:1");

总结

这段代码的核心作用:

  1. setVisibility:让 Jackson 能访问 private 字段
  2. activateDefaultTyping在 JSON 中存储类型信息(最重要!)
  3. 目的:解决反序列化时类型丢失的问题

不配置:取出来是 LinkedHashMap
配置后:取出来是原始的 User 对象

到此这篇关于SpringBoot18 redis的配置方法的文章就介绍到这了,更多相关springboot redis配置内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于SpringBoot18 redis的配置方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156220

相关文章

检查 Nginx 是否启动的几种方法

《检查Nginx是否启动的几种方法》本文主要介绍了检查Nginx是否启动的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1. 使用 systemctl 命令(推荐)2. 使用 service 命令3. 检查进程是否存在4

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

MySQL字符串转数值的方法全解析

《MySQL字符串转数值的方法全解析》在MySQL开发中,字符串与数值的转换是高频操作,本文从隐式转换原理、显式转换方法、典型场景案例、风险防控四个维度系统梳理,助您精准掌握这一核心技能,需要的朋友可... 目录一、隐式转换:自动但需警惕的&ld编程quo;双刃剑”二、显式转换:三大核心方法详解三、典型场景

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

MySQL快速复制一张表的四种核心方法(包括表结构和数据)

《MySQL快速复制一张表的四种核心方法(包括表结构和数据)》本文详细介绍了四种复制MySQL表(结构+数据)的方法,并对每种方法进行了对比分析,适用于不同场景和数据量的复制需求,特别是针对超大表(1... 目录一、mysql 复制表(结构+数据)的 4 种核心方法(面试结构化回答)方法 1:CREATE

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

springboot的controller中如何获取applicatim.yml的配置值

《springboot的controller中如何获取applicatim.yml的配置值》本文介绍了在SpringBoot的Controller中获取application.yml配置值的四种方式,... 目录1. 使用@Value注解(最常用)application.yml 配置Controller 中

springboot中配置logback-spring.xml的方法

《springboot中配置logback-spring.xml的方法》文章介绍了如何在SpringBoot项目中配置logback-spring.xml文件来进行日志管理,包括如何定义日志输出方式、... 目录一、在src/main/resources目录下,也就是在classpath路径下创建logba

SQL Server中行转列方法详细讲解

《SQLServer中行转列方法详细讲解》SQL行转列、列转行可以帮助我们更方便地处理数据,生成需要的报表和结果集,:本文主要介绍SQLServer中行转列方法的相关资料,需要的朋友可以参考下... 目录前言一、为什么需要行转列二、行转列的基本概念三、使用PIVOT运算符进行行转列1.创建示例数据表并插入数