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的实例。想到这里,就需要翻下官网的文档,看看官网文档有没有什么说明。
我框出来的这句话特别重要,大概中文翻译如下:
如果你自己在配置类里面注入了一个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都是采用此策略序列化保存的。