[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f2qpn4gta07ang":3},{"_id":4,"slug":5,"title":6,"subtitle":7,"kind":8,"cards":9,"tags":43,"categories":44,"source":45,"lang":48,"author":49,"audioState":52,"stats":53,"publishedAt":56,"renderer":57},"6abd0024ca21c797c7ea16e8","spring-boot-redis-redistemplate-2fb24952","为什么 Spring Boot 自动配置了 Redis，还要自己写 RedisTemplate？","Spring Boot 中 RedisTemplate 是如何生效的？从 @Configuration、@Bean 到依赖注入 在项目里接入 Redis 的时候，我遇到了一个比较困惑的问题： Spring Boot 已经自动配置 Redis 了，为什么还需要自己定义一个 RedisTemplate\u003CString, Object>？ Spring Boot 已经自动配置 Redis…","news",[10,14,19,24,29,34,38],{"headline":6,"body":11,"imageUrl":12,"images":13},"Spring Boot 中 RedisTemplate 是如何生效的？从 @Configuration、@Bean 到依赖注入 在项目里接入 Redis 的时候，我遇到了一个比较困惑的问题： Spring Boot 已经自动配置 Redis 了，为什么还需要自己定义一个 RedisTemplate\u003CString, Object>？ Spring Boot 已经自动配置 Redis 了，为什么还需要自己定义一个 RedisTemplate\u003CString, Object>？ 答案其实很简单：为了让 Redis 里存的数据是能看懂的。","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F0.webp",{"local":12},{"headline":15,"body":16,"imageUrl":17,"images":18},"Spring Boot 默认提供的 RedisTemplate 用的是 JDK 二进制序列化，存进去的 key","Spring Boot 默认提供的 RedisTemplate 用的是 JDK 二进制序列化，存进去的 key 带一串乱码前缀，value 更是一堆看不懂的二进制。所以我加了这个配置类，把 key 换成字符串序列化、value 换成 JSON 序列化。 但真正让我困惑的是：这个配置类到底是怎么生效的？它凭什么能顶掉自动配置里那个 RedisTemplate？ 顺着这个问题，我把 Spring 的 Bean 注册与注入机制从头理了一遍，这篇文章就是整理的结果。 先不要急着看代码，可以把 Spring Boot 启动过程简化成下面这样： 所以真正起作用的并不是 RedisConfig 自己主动调用了什么，而是： Spring 在启动的时候发现了这个配置类，然后根据 @Bean 创建对象并放进 IoC 容器。 Spring 在启动的时候发现了这个配置类，然后根据 @Bean 创建对象并放进 IoC 容器。 @Configuration 的作用可以简单理解成： 告诉 Spring：这是一个配置类，请处理这个类中的 Bean 定义。 告诉 Spring：这是一个配置类，请处理这个类中的 Bean 定义。 这就涉及 @SpringBootApplication。 @SpringBootApplication 本身包含了组件扫描能力。 三、@Bean 才是真正创建 RedisTemplate 的地方 四、为什么 RedisConnectionFactory 不需要自己创建？ 因为 Spring Boot 的 Redis 自动配置已经帮我们创建好了相关 Bean。 五、所以 RedisTemplate\u003CString,Object> 到底是什么？ 执行完配置类之后，可以简单认为 Spring 容器里面多了一个 Bean： 默认情况下，@Bean 方法名就是 Bean 名称。 既然这个对象已经放进 Spring IoC 容器，那么其他组件就可以使用它。 Spring 就会从容器中寻找符合条件的 Bean。 @Bean 负责把对象放进去，@Autowired \u002F @Resource","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F1.webp",{"local":17},{"headline":20,"body":21,"imageUrl":22,"images":23},"负责把对象拿出来。 @Bean 负责把对象放进去，@Autowired \u002F @Resource 负责把对象拿出来。 这是理解 Spring","负责把对象拿出来。 @Bean 负责把对象放进去，@Autowired \u002F @Resource 负责把对象拿出来。 这是理解 Spring IoC 的一个非常重要的思路。 七、RedisTemplate\u003CObject,Object> 和 RedisTemplate\u003CString,Object> 有什么区别？ Spring Boot 的 RedisAutoConfiguration 源码是这样的： Spring Boot 想提供的默认值是 RedisTemplate\u003CObject, Object>，而用户自定义的通常是 RedisTemplate\u003CString, Object>。","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F2.webp",{"local":22},{"headline":25,"body":26,"imageUrl":27,"images":28},"假设这里按类型判断：RedisTemplate\u003CString, Object> 的运行时类型也是 RedisTemplate，条件会认为\"容器里已经有…","假设这里按类型判断：RedisTemplate\u003CString, Object> 的运行时类型也是 RedisTemplate，条件会认为\"容器里已经有 RedisTemplate 了\"，于是自动配置的照常创建 —— 用户的自定义就永远覆盖不掉默认值。 所以 Spring Boot 特意写成 name = \"redisTemplate\"： 只要你定义了一个名字叫 redisTemplate 的 Bean，不管泛型写成什么，我都让位。 只要你定义了一个名字叫 redisTemplate 的 Bean，不管泛型写成什么，我都让位。 @Bean 不指定名字时，方法名就是 Bean 名称，所以注册出来的 Bean 名字正好是 redisTemplate。 所以容器里始终只有一个 RedisTemplate，就是我们自己定义的那个。 那么泛型的区别体现在哪里？体现在注入时能不能匹配上： 这也是为什么当某个组件明确要求 RedisTemplate\u003CString,Object> 时，不能简单认为： “反正已经有一个 RedisTemplate 了，直接用不就行了？” “反正已经有一个 RedisTemplate 了，直接用不就行了？” 不一定 —— 容器里那个默认的 RedisTemplate\u003CObject,Object> 泛型对不上，注入会失败。而我们的配置之所以能覆盖掉默认值，靠的正是 Bean 名称相同。 八、@Autowired 和 @Resource 有什么区别？ 九、什么时候才会真的出现两个 RedisTemplate？ 前面说过，只要 Bean 名字叫 redisTemplate，自动配置就会让位。那反过来 —— 如果名字不叫 redisTemplate 呢？ 这时 Bean 名称变成 myRedisTemplate，redisTemplate 这个名字就空出来了，自动配置的条件重新满足，于是两个 Bean 会同时存在： 这时才轮到泛型匹配出场。注入 RedisTemplate\u003CString,Object> 时： Spring 会按泛型筛选，只有","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F3.webp",{"local":27},{"headline":30,"body":31,"imageUrl":32,"images":33},"myRedisTemplate 的泛型完全匹配，所以能正常注入。 小结：泛型匹配和 @Qualifier…","myRedisTemplate 的泛型完全匹配，所以能正常注入。 小结：泛型匹配和 @Qualifier 只在\"容器里真的存在多个候选\"时才有意义。默认情况下，因为我们占用了 redisTemplate 这个名字，容器里只有一个 RedisTemplate，根本不会冲突。 Spring Boot 的自动配置和我们的自定义配置，是\"默认值\"与\"覆盖值\"的关系：一旦我们自己提供了同名的 Bean，自动配置就会主动让位，而不是两者并存。 Spring Boot 的自动配置和我们的自定义配置，是\"默认值\"与\"覆盖值\"的关系：一旦我们自己提供了同名的 Bean，自动配置就会主动让位，而不是两者并存。 前面说过，这个配置类的作用是换掉序列化方式。但有一点可能反直觉： 我把 RedisConfig 上的 @Configuration 注释掉，重新编译启动： 原因很简单：Spring Boot 的自动配置本来就提供了 RedisTemplate\u003CObject, Object>，容器里从来就不缺 RedisTemplate。我们自定义的那个，只是把它换掉了。 关键在于 Spring Boot 默认的 RedisTemplate\u003CObject, Object> 用的是 JdkSerializationRedisSerializer，它会把对象按 Java 的二进制格式写进 Redis。 key 前面多了一串二进制前缀，value 是一堆看不懂的乱码。 没法排查问题 —— 线上出问题时用 redis-cli 连上去，什么都看不出来 跨语言不兼容 —— 其他语言的服务读到这串二进制没法解析 存储空间浪费 —— 二进制序列化会带上类名、类型信息等额外开销 key 是普通字符串，value 是能直接看懂的 JSON。 它不是为了让程序能跑起来（不写也能跑），而是为了让 Redis 里的数据可读、可排查、跨语言兼容。 顺带一提，afterPropertiesSet() 也别漏 —— 它负责校验 connectionFactory 是否设置、初始化序列化器，不调用的话部分配置不会生效。 写成","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F4.webp",{"local":32},{"headline":35,"body":35,"imageUrl":36,"images":37},"name = \"xxx\" 时按名称判断，不写时按类型判断。","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F5.webp",{"local":36},{"headline":39,"body":40,"imageUrl":41,"images":42},"Spring Boot 默认提供的 RedisTemplate\u003CObject,Object> 用的是 JDK 二进制序列化，存进 Redis","Spring Boot 默认提供的 RedisTemplate\u003CObject,Object> 用的是 JDK 二进制序列化，存进 Redis 的数据在 redis-cli 里是一堆乱码，满足不了业务需要。于是我们通过 @Configuration + @Bean 定义了一个名为 redisTemplate 的 RedisTemplate\u003CString,Object>（String key + JSON value）。名字相同，自动配置就会让位，最终容器里生效的就是我们自定义的这个。","\u002Fapi\u002Fmedia\u002Fposts\u002Fspring-boot-redis-redistemplate-2fb24952\u002F6.webp",{"local":41},[],[],{"name":46,"url":47},"Juejin","https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7690497906533138482","zh",{"handle":50,"displayName":51},"spots","Spots",null,{"views":54,"likes":55,"saves":55,"shares":55,"completions":55,"opens":55,"skips":55,"depthSum":55},1,0,"2026-09-30T12:27:16.468Z","local"]