1.问题引入
在java里使用redis非常简单,只需要引入依赖后生成一个RedisTemplate对象即可,然后就可以在其他类里调用对应的put,get,delete方法对redis进行操作
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
return template;
}
}
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void saveData(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public Object getData(String key) {
redisTemplate.opsForValue().get(key);
}
public void deleteData(String key) {
redisTemplate.delete(key);
}
但是这样子也出现了一个问题,没法规范redis的使用
redis存储了键值对,key一般是string类型的,但是会有多个参数进行格式化;value的类型更是多种多样的
如果没有统一的配置,key的格式化错误会导致无法get到put的值;而如果在put的时候上传了A类型,在get的时候转换为B类型,则会出现报错的情况
即使没有出现报错,也不利于后续维护
2.解决方法
对redis的操作进行二次封装是一个不错的解决方法
首先创建一个类定义redis的键值对,里面定义了key的格式化模板,redis的过期时间,备注,使用泛型则支持在实例化时对value的class进行指定
@Data
public class RedisKeyDefine<T> {
/**
* Key 模板
*/
private final String keyTemplate;
/**
* 过期时间
*/
private final Duration timeout;
/**
* 备注
*/
private final String memo;
RedisKeyDefine(String memo, String keyTemplate, Duration timeout) {
this.memo = memo;
this.keyTemplate = keyTemplate;
this.timeout = timeout;
}
/**
* 格式化 Key
* <p>
* 注意,内部采用 {@link String#format(String, Object...)} 实现
*
* @param args 格式化的参数
* @return Key
*/
public String formatKey(Object... args) {
return String.format(keyTemplate, args);
}
}
然后在常量类里定义各种不同的redis键值对,在该类里,redis的键值对信息一目了然
public class RedisKeyConstants {
public static final RedisKeyDefine<AccessTokenInfoDO> OAUTH2_ACCESS_TOKEN =
new RedisKeyDefine<>("访问令牌的缓存",
"oauth2_access_token:%s",
Duration.ofHours(8));
public static final RedisKeyDefine<ThirdUserDetail> THIRD_USER_DETAIL =
new RedisKeyDefine<>("三方用户信息",
"third_user_detail:%s",
Duration.ofHours(1));
}
然后封装一个redistemplate进行get,put,delete操作
然后封装一个redistemplate进行get,put,delete操作
@Component
@Log4j2
public class ObjectRedisTemplate {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public ObjectRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.json());
template.setHashValueSerializer(RedisSerializer.json());
template.afterPropertiesSet();
this.redisTemplate = template;
}
public <T> T get(RedisKeyDefine<T> define, String key) {
ValueOperations<String, T> redis = (ValueOperations<String, T>) redisTemplate.opsForValue();
return redis.get(define.formatKey(key));
}
public <T> void put(RedisKeyDefine<T> define, String key, T value) {
ValueOperations<String, T> redis = (ValueOperations<String, T>) redisTemplate.opsForValue();
redis.set(define.formatKey(key), value, define.getTimeout());
}
public <T> void delete(RedisKeyDefine<T> define, String key) {
redisTemplate.delete(define.formatKey(key));
}
}
3.使用示例
使用方法也很简单,注入ObjectRedisTemplate后,执行put,get,delete方法即可,额外增加的RedisKeyConstants使得在操作时无需花费精力去关注redis里存储的格式,减少了出错可能
@Autowired
private ObjectRedisTemplate redisTemplate;
//上传
AccessTokenInfoDO accessTokenDO = getAccessTokenInfo(tokenDO);
redisTemplate.put(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken, accessTokenDO);
//下载
AccessTokenInfoDO accessTokenDO = redisTemplate.get(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);
//删除
redisTemplate.delete(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);