【小家Spring】Spring Boot中使用RedisTemplate优雅的操作Redis,并且解决RedisTemplate泛型注入失败的问题(中)

简介: 【小家Spring】Spring Boot中使用RedisTemplate优雅的操作Redis,并且解决RedisTemplate泛型注入失败的问题(中)

RedisTemplate<String, String>和StringRedisTemplate是否相同呢?




基于这个疑问,做出如下测试:


    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Test
    public void contextLoads() {
        System.out.println(redisTemplate == stringRedisTemplate); //true
    }


答案:实际注入的就是stringRedisTemplate对象。因为Spring的Bean默认都是单例的,所以返回true。源码一探究竟:

public class StringRedisTemplate extends RedisTemplate<String, String> {}


相信大家看到此处,就不用小编做过多的解释了。


RedisTemplate注入泛型为任意类型的实例,怎么办呢?


问题来了,我们从源码可以看出,Boot默认只为我们注入两个Bean,一个StringRedisTempate专门处理最常用的key和value都是String类型的。另外一个是RedisTemplate<Object, Object>,虽然能处理一切类型,但有时候使用起来确实不方便,我们希望在编译期就能知道泛型类型,写出更健壮的代码。


我们试试这么来:洗希望我的value对象是Person类型

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Person implements Serializable {
    private String name;
    private Integer age;
}


代码如下:


    @Autowired
    private RedisTemplate<String, Person> redisTemplate;
    @Test
    public void contextLoads() {
        System.out.println(redisTemplate);
    }


启动发现报错:


org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.fsx.run2.Run2ApplicationTests': Unsatisfied dependency expressed through field 'redisTemplate'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.data.redis.core.RedisTemplate<java.lang.String, com.fsx.run2.bean.Person>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
  at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]


报错原因也容易看懂:找不到类型为RedisTemplate<String, Person>的Bean


那么这个问题该如何解决呢?不妨在使用RedisTemplate< K, V>时不指定具体的类型,修改代码如下:注入时不指定K、V的类型


    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void contextLoads() {
        System.out.println(redisTemplate); //org.springframework.data.redis.core.RedisTemplate@2c2c3947
    }


重启服务,发现没有报错了,注入成功。(这种解决方案其实也是最常用的解决方案)


RedisTemplate为什么会注入失败呢?


想到RedisTemplate在SpringBoot框架中是自动配置的,容器中默认的就是RedisTemplate的实例。想到这里,就需要翻下官网的文档,看看官网文档有没有什么说明。

image.png



我框出来的这句话特别重要,大概中文翻译如下:


如果你自己在配置类里面注入了一个Bean,那么将会替换Boot默认注册的Bean(您注入的bean的名称只有是restTemplate才会替换默认的,否则不替换)


请参加@ConditionalOnMissingBean这个注解,足以看见Spring的设计者的设计思想还是非常不错的:对修改关闭,对扩展开放。


我们做如下实验一:(bean的名字不要叫redisTemplate,否则Boot的Bean就不会再注入了)

自己注册一个bean

    @Bean("myRedisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }


测试代码如下:


    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void contextLoads() {
        Object redisTemplate1 = applicationContext.getBean("redisTemplate");
        Object myRedisTemplate2 = applicationContext.getBean("myRedisTemplate");
        System.out.println(redisTemplate1); //RedisTemplate@5ec6a1b6
        System.out.println(myRedisTemplate2); //RedisTemplate@40013051
        //我们发现这样注入进来的是Boot默认注册的那个bean
        System.out.println(this.redisTemplate); //RedisTemplate@5ec6a1b6
    }


惊奇一:Spring容器内出现了多个类型一样的Bean,但Autowaired竟然成功了(这个绝提原因,读者可以去了解一下Spring4开始的泛型依赖注入,Spring在这方面有处理)

惊奇二:Spring自动注入,注入的为Boot给自动注入进去的Bean,而不是我们自己注入的(我感觉这应该和先后顺序有管。并没有强制的识别“系统”,虽然我没看源码,但若有研究过的,可以跟我讨论)


我们改一下,把我们自己注入的bean泛型改一下:


    @Bean("myRedisTemplate")
    public RedisTemplate<String, Person> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Person> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }


测试代码:


    @Test
    public void contextLoads() {
        Object redisTemplate1 = applicationContext.getBean("redisTemplate");
        Object myRedisTemplate2 = applicationContext.getBean("myRedisTemplate");
        System.out.println(redisTemplate1); //RedisTemplate@2c2c3947
        System.out.println(myRedisTemplate2); //RedisTemplate@4a62062a
        System.out.println(this.redisTemplate); //RedisTemplate@2c2c3947
        System.out.println(this.personRedisTemplate); //edisTemplate@4a62062a
    }


由此我们看出,Spring很好的实现了泛型依赖注入。很强大有木有,太方便了。虽然类型都一样,但是泛型不一样,强大的Spring还是能够很好的区分开。


那么在实际的开发中,如果我们需要操作上面Person类型,怎么做呢?


建议:注入restTemplate的时候不指定类型,而在在获取处理器的时候,手动指定泛型即可

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void contextLoads() {
        ValueOperations<String, Person> valueOperations = redisTemplate.opsForValue();
        valueOperations.set("aaa", new Person("fsx", 24));
        Person p = valueOperations.get("aaa"); //Person(name=fsx, age=24)
        System.out.println(p);
    }


我们发现不需要特意的去指定restTemplate的泛型,而是在获得处理器的时候指定就好了。因为restTemplate默认可以处理一切类型。(Spring内部做了类型强转,所以不会出问题)


当然,你自己注册一个指定类型,也是没毛病的


StringRedisTemplate与RedisTemplate的区别


1.两者的关系是StringRedisTemplate继承RedisTemplate。


2.两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。


3.默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。


4。StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。


相关文章
|
4月前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
4月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
4月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
565 2
|
5月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
4587 2
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
存储 JSON Java
711 0
|
5月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
443 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
5月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1964 0
|
6月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1079 3