为什么要这个通用缓存Key?
当项目中的模块越来越多的时候,需要存的缓存也越来越多,比如商品Id,订单Id,用户id等,此时若是id出现重复,将给系统带来错误。
那么使用KeyPrefix来更好的操作和管理缓存中对应的key。给不同模块的key带有一个前缀。
不加前缀的情况:
public <T> boolean set(String key,T value){
Jedis jedis=null;
//在JedisPool里面取得Jedis
try {
jedis=jedisPool.getResource();
//将T类型转换为String类型
String s=beanToString(value);
if(s==null) {
return false;
}
jedis.set(key, s);
return true;
}finally {
returnToPool(jedis);
}
}
解决覆盖key的办法:
将前缀+一个key一起作为一个redis里面真正的key,这样不同模块之间就不会重复。
好处:不同功能或者不同模块的前缀不同,即使有同名key出现,那么前缀不同,并不会引起key冲突被其他功能覆盖的情况。
使用一个模板模式来封装:
接口:
/**
*做缓存的前缀接口
*/
public interface KeyPrefix {
//有效期
public int expireSeconds();
//前缀
public String getPrefix();
}
BasePrefix 抽象类:简单的实现一下KeyPrefix,定义成抽象类原因,防止不小心被创建,我们不希望BasePrefix被实例化,因为抽象类不允许实例化。我们只希望它被继承。不同模块的前缀类都继承他。
//定义成抽象类
public abstract class BasePrefix implements KeyPrefix{
private int expireSeconds;
private String prefix;
public BasePrefix() {
}
public BasePrefix(String prefix) {
//this(0, prefix);//默认使用0,不会过期
this.expireSeconds=0;
this.prefix=prefix;
}
public BasePrefix(int expireSeconds,String prefix) {//覆盖了默认的构造函数
this.expireSeconds=expireSeconds;
this.prefix=prefix;
}
//默认为0代表永不过期
public int expireSeconds() {
return expireSeconds;
}
//前缀为类名:+prefix
public String getPrefix() {
String className=getClass().getSimpleName();
return className+":"+prefix;
}
}
注意:该类2种不同构造方法:用于继承。一个只带前缀名,一个带前缀名和过期时间。当实现public BasePrefix(String prefix)的时候,我们将默认这个key不会失效,因为有一些场景,我们不希望key失效,但是有些场景我们需要设置key的合适的有效期。
具体实现类:
用户UserKey只去继承了super(prefix),即public BasePrefix(String prefix),那么代表user的key的过期时间为不会过期。
public class UserKey extends BasePrefix{
public UserKey(String prefix) {
super(prefix);
}
public static UserKey getById=new UserKey("id");
public static UserKey getByName=new UserKey("name");
}
秒杀用户的MiaoshaUserKey ,继承了super(expireSeconds,prefix),可以设置有效期时间为2天。
public class MiaoshaUserKey extends BasePrefix{
public static final int TOKEN_EXPIRE=3600*24*2;//3600S*24*2 =2天
public MiaoshaUserKey(int expireSeconds,String prefix) {
super(expireSeconds,prefix);
}
public static MiaoshaUserKey token=new MiaoshaUserKey(TOKEN_EXPIRE,"tk");
//对象缓存一般没有有效期,永久有效
public static MiaoshaUserKey getById=new MiaoshaUserKey(0,"id");
}
具体实现类的具体使用场景:
/**
*避免key被不同类的数据覆盖
*使用Prefix前缀-->不同类别的缓存,用户、部门、
*/
@RequestMapping("/redis/set")
@ResponseBody
public Result<Boolean> redisSet() {//0代表成功
User user=new User(1,"1111");
boolean f=redisService.set(UserKey.getById,""+1,user);
return Result.success(true);
}
@RequestMapping("/redis/getbyid")
@ResponseBody
public Result<User> redisGetById() {//0代表成功
User res=redisService.get(UserKey.getById,""+1,User.class);
//redisService.get("key1",String.class);
//System.out.println("res:"+userService.tx());
return Result.success(res);
}
完善后的get和set缓存的Key值的方法:
public <T> T get(KeyPrefix prefix,String key,Class<T> data){
Jedis jedis=null;
//在JedisPool里面取得Jedis
try {
jedis=jedisPool.getResource();
//生成真正的key className+":"+prefix; BasePrefix:id1
String realKey=prefix.getPrefix()+key;
//System.out.println("jedis:"+jedis);
String sval=jedis.get(realKey);
//将String转换为Bean入后传出
T t=stringToBean(sval,data);
return t;
}finally {
returnToPool(jedis);
}
}
/**
* 设置单个、多个对象
*/
//MiaoshaUserKey.token, token, user
public <T> boolean set(KeyPrefix prefix,String key,T value){
Jedis jedis=null;
try {
//在JedisPool里面取得Jedis
jedis=jedisPool.getResource();
String realKey=prefix.getPrefix()+key;
String s=beanToString(value);
if(s==null||s.length()<=0) {
return false;
}
int seconds=prefix.expireSeconds();
if(seconds<=0) {//有效期:代表不过期,这样才去设置
jedis.set(realKey, s);
}else {//没有设置过期时间,没有设置有效期,自己设置
jedis.setex(realKey, seconds,s);
}
return true;
}finally {
returnToPool(jedis);
}
}