在 spring boot 项目中使用了 redis,在此对 RedisTemplate 的使用做个记录,另外在腾讯云上安装了 redis 作为缓存服务器进行了测试。
RedisTemplate 的使用
首先,redis 提供了 RedisTemplate<Object,Object> 和 StringRedisTemplate 两个默认实现,其中 StringRedisTemplate 继承自 RedisTemplate。 RedisTemplate<Object,Object> 使用序列化的方式存储 key 和 value,StringRedisTemplate 则是一个 key 和 value 都是 String 的 RedisTemplate<String,String>。
对以上两个类的使用不再叙述。看一下以下错误的解决情况,并对应源码简单分析:
There is more than one beans ‘RedisTemplate’ type
在尝试直接使用以下代码注入 RedisTemplate,会有如上报错,
1 2
| @Autowired private RedisTemplate mRedisTemplate;
|
这是因为 RedisTemplate 使用了泛型,此时框架无法确定需要注入的是 redisTemplate 还是 stringRedisTemplate。因此,使用 RedisTemplate 时需要指定泛型。
使用自定义实体类时, No beans of ‘RedisTemplate<String,City>’ type found
我们建立如下实体类 City,并且实现了 Serializable 接口,以在 RedisTemplate 中使用二进制序列化的方式存储 City 实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Entity public class City implements Serializable {
Integer id; Integer provinceId; String cityName; String description;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getProvinceId() { return provinceId; }
public void setProvinceId(int provinceId) { this.provinceId = provinceId; }
public String getCityName() { return cityName; }
public void setCityName(String cityName) { this.cityName = cityName; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
|
然后尝试使用如下代码注入 RedisTemplate<String,City>,
1 2
| @Autowired private RedisTemplate<String, City> mRedisTemplate;
|
会报错‘No beans of ‘RedisTemplate<String,City>’ type found’,这是因为 redis 没有注册这一类型的 bean,需要我们增加添加这一bean。
我们可以创建一个 RedisConfig 类,在其中进行配置。
首先,为了自定义 City 类的序列化行为,我们可以继承 RedisSerializer 实现类 MyRedisSerializer 自定义序列化行为,只需要实现接口的 serialize() 方法和 deserialize() 方法,不再叙述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class RedisConfig {
@Bean public RedisTemplate<String, City> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, City> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new MyRedisSerializer()); template.afterPropertiesSet(); return template; }
}
|
这样我们就实现了 RedisTemplate<String, City> 这一 bean,而且使用自定义的 MyRedisSerializer 类的序列化规则对 City 进行序列化。
关于这一配置 bean 的方法,其原理我们可以查看 RedisAutoConfiguration 类,其中有如下代码:
1 2 3 4 5 6 7 8
| @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; }
|
这里的关键是 @ConditionalOnMissingBean(name = “redisTemplate”) 注解,注解的作用是:如果用户没有配置 name 为 redisTemplate 的 bean,那么使用此方法定义的 RedisTemplate<Object, Object> 进行注入,否则使用用户配置的 bean。
所以,我们可以用之前的代码自定义一个 bean,实现 RedisTemplate<String, City>。
自定义 StringRedisTemplate
如果我们想自己实现 StringRedisTemplate,如上,我们可以 RedisAutoConfiguration 类看到如下代码:
1 2 3 4 5 6 7 8
| @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
|
这里仍然是使用了 @ConditionalOnMissingBean 注解,所以我们需要按照如下代码来实现:
1 2 3 4 5
| @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { }
|
接下来注入和使用 StringRedisTemplate 实例,使用断点或者 log 等可以看到,此时注入的 StringRedisTemplate 是我们自己配置的实例。
要注意的是,只有我们使用了 RedisConnectionFactory 对 StringRedisTemplate 进行了自定义配置时并调用了 template.afterPropertiesSet() 方法使配置生效后,我们的 StringRedisTemplate 才与默认的 StringRedisTemplate 不同,才可以使用,不然项目会启动失败。另外,若只是需要进行指定序列化方式等,RedisTemplate 类本身就提供了相应方法,不必为此自行配置 bean。
使用代码进行 Redis 的配置
如果我们想对 配置文件中的 redis 配置项进行覆盖,或者我们不想使用配置文件,想完全使用代码进行配置,可以使用如下代码:
1 2 3 4 5 6 7 8 9 10 11
| @Configuration public class RedisConfig { @Bean RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration); factory.afterPropertiesSet(); return factory; } }
|
这样,我们在 configuration 实例中设置的项会覆盖掉配置文件中内容和默认内容。这里的原理上文提到的相同,我们可以在 LettuceConnectionConfiguration 类中看到如下代码:
1 2 3 4 5 6 7 8
| @Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) public LettuceConnectionFactory redisConnectionFactory( ClientResources clientResources) throws UnknownHostException { LettuceClientConfiguration clientConfig = getLettuceClientConfiguration( clientResources, this.properties.getLettuce().getPool()); return createLettuceConnectionFactory(clientConfig); }
|
仍然是 @ConditionalOnMissingBean 注解的作用。