Spots

为什么 Spring Boot 自动配置了 Redis,还要自己写 RedisTemplate?

Spring Boot 中 RedisTemplate 是如何生效的?从 @Configuration、@Bean 到依赖注入 在项目里接入 Redis 的时候,我遇到了一个比较困惑的问题: Spring Boot 已经自动配置 Redis 了,为什么还需要自己定义一个 RedisTemplate<String, Object>? Spring Boot 已经自动配置 Redis 了,为什么还需要自己定义一个 RedisTemplate<String, Object>? 答案其实很简单:为了让 Redis 里存的数据是能看懂的。

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<String,Object> 到底是什么? 执行完配置类之后,可以简单认为 Spring 容器里面多了一个 Bean: 默认情况下,@Bean 方法名就是 Bean 名称。 既然这个对象已经放进 Spring IoC 容器,那么其他组件就可以使用它。 Spring 就会从容器中寻找符合条件的 Bean。 @Bean 负责把对象放进去,@Autowired / @Resource

负责把对象拿出来。 @Bean 负责把对象放进去,@Autowired / @Resource 负责把对象拿出来。 这是理解 Spring

负责把对象拿出来。 @Bean 负责把对象放进去,@Autowired / @Resource 负责把对象拿出来。 这是理解 Spring IoC 的一个非常重要的思路。 七、RedisTemplate<Object,Object> 和 RedisTemplate<String,Object> 有什么区别? Spring Boot 的 RedisAutoConfiguration 源码是这样的: Spring Boot 想提供的默认值是 RedisTemplate<Object, Object>,而用户自定义的通常是 RedisTemplate<String, Object>。

假设这里按类型判断:RedisTemplate<String, Object> 的运行时类型也是 RedisTemplate,条件会认为"容器里已经有…

假设这里按类型判断:RedisTemplate<String, Object> 的运行时类型也是 RedisTemplate,条件会认为"容器里已经有 RedisTemplate 了",于是自动配置的照常创建 —— 用户的自定义就永远覆盖不掉默认值。 所以 Spring Boot 特意写成 name = "redisTemplate": 只要你定义了一个名字叫 redisTemplate 的 Bean,不管泛型写成什么,我都让位。 只要你定义了一个名字叫 redisTemplate 的 Bean,不管泛型写成什么,我都让位。 @Bean 不指定名字时,方法名就是 Bean 名称,所以注册出来的 Bean 名字正好是 redisTemplate。 所以容器里始终只有一个 RedisTemplate,就是我们自己定义的那个。 那么泛型的区别体现在哪里?体现在注入时能不能匹配上: 这也是为什么当某个组件明确要求 RedisTemplate<String,Object> 时,不能简单认为: “反正已经有一个 RedisTemplate 了,直接用不就行了?” “反正已经有一个 RedisTemplate 了,直接用不就行了?” 不一定 —— 容器里那个默认的 RedisTemplate<Object,Object> 泛型对不上,注入会失败。而我们的配置之所以能覆盖掉默认值,靠的正是 Bean 名称相同。 八、@Autowired 和 @Resource 有什么区别? 九、什么时候才会真的出现两个 RedisTemplate? 前面说过,只要 Bean 名字叫 redisTemplate,自动配置就会让位。那反过来 —— 如果名字不叫 redisTemplate 呢? 这时 Bean 名称变成 myRedisTemplate,redisTemplate 这个名字就空出来了,自动配置的条件重新满足,于是两个 Bean 会同时存在: 这时才轮到泛型匹配出场。注入 RedisTemplate<String,Object> 时: Spring 会按泛型筛选,只有

myRedisTemplate 的泛型完全匹配,所以能正常注入。 小结:泛型匹配和 @Qualifier…

myRedisTemplate 的泛型完全匹配,所以能正常注入。 小结:泛型匹配和 @Qualifier 只在"容器里真的存在多个候选"时才有意义。默认情况下,因为我们占用了 redisTemplate 这个名字,容器里只有一个 RedisTemplate,根本不会冲突。 Spring Boot 的自动配置和我们的自定义配置,是"默认值"与"覆盖值"的关系:一旦我们自己提供了同名的 Bean,自动配置就会主动让位,而不是两者并存。 Spring Boot 的自动配置和我们的自定义配置,是"默认值"与"覆盖值"的关系:一旦我们自己提供了同名的 Bean,自动配置就会主动让位,而不是两者并存。 前面说过,这个配置类的作用是换掉序列化方式。但有一点可能反直觉: 我把 RedisConfig 上的 @Configuration 注释掉,重新编译启动: 原因很简单:Spring Boot 的自动配置本来就提供了 RedisTemplate<Object, Object>,容器里从来就不缺 RedisTemplate。我们自定义的那个,只是把它换掉了。 关键在于 Spring Boot 默认的 RedisTemplate<Object, Object> 用的是 JdkSerializationRedisSerializer,它会把对象按 Java 的二进制格式写进 Redis。 key 前面多了一串二进制前缀,value 是一堆看不懂的乱码。 没法排查问题 —— 线上出问题时用 redis-cli 连上去,什么都看不出来 跨语言不兼容 —— 其他语言的服务读到这串二进制没法解析 存储空间浪费 —— 二进制序列化会带上类名、类型信息等额外开销 key 是普通字符串,value 是能直接看懂的 JSON。 它不是为了让程序能跑起来(不写也能跑),而是为了让 Redis 里的数据可读、可排查、跨语言兼容。 顺带一提,afterPropertiesSet() 也别漏 —— 它负责校验 connectionFactory 是否设置、初始化序列化器,不调用的话部分配置不会生效。 写成

name = "xxx" 时按名称判断,不写时按类型判断。

name = "xxx" 时按名称判断,不写时按类型判断。

Spring Boot 默认提供的 RedisTemplate<Object,Object> 用的是 JDK 二进制序列化,存进 Redis

Spring Boot 默认提供的 RedisTemplate<Object,Object> 用的是 JDK 二进制序列化,存进 Redis 的数据在 redis-cli 里是一堆乱码,满足不了业务需要。于是我们通过 @Configuration + @Bean 定义了一个名为 redisTemplate 的 RedisTemplate<String,Object>(String key + JSON value)。名字相同,自动配置就会让位,最终容器里生效的就是我们自定义的这个。

News

为什么 Spring Boot 自动配置了 Redis,还要自己写 RedisTemplate?

Spring Boot 中 RedisTemplate 是如何生效的?从 @Configuration、@Bean 到依赖注入 在项目里接入 Redis 的时候,我遇到了一个比较困惑的问题: Spring Boot 已经自动配置 Redis 了,为什么还需要自己定义一个 RedisTemplate<String, Object>? Spring Boot 已经自动配置 Redis…

@spots
Source: Juejin
See more like this