前段时间在回顾自己项目的时候发现了一些问题,并对这些问题进行了优化,今天就来记录一下对redis使用的一些优化
redis一般情况来说用作缓存,会存储一些需要缓存的对象信息,但是当存储的内容过多以后,如何命名这些缓存的对象,能够快速地查找出你需要的对象,快速地区分哪些key里面存的是你想要的数据就成了一个问题。
举个例子:可能你今天码代码的时候觉得当存放user对象时key值应该是“user“,过几天回过头来继续写的时候又觉得key值应该写成“USER”,这个时候你还要去查找你上次写的代码的地方,和你原来书写的值做一个统一,统一这种缓存存值的命名规范,感觉是一种无脑操作,浪费时间浪费精力,还经常想当然地把命名搞错了,及其不方便
并且还会出现你查看缓存的时候可能根本不忘了这个缓存key是用来缓存什么东西了,他是属于哪个类的都不知道
这样极大地增加了自己的工作量,并且不够严谨
当时做这个项目的时候不觉得有什么问题,但是这次的目的是为了优化代码结构,写出一个规范的漂亮的不冗余的代码,这个问题就需要解决了。
我的解决方式也狠简单,使用了模板模式,针对存放对象的类不同来统一加上类名作为前缀,并且后面跟上自己自定义的名字,然后根据每一个自己想缓存的对象的所属的类去实现不同的构造名字的方法:
第一步:首先定义了一个接口,里面呢包含两个方法,设置过期时间和前缀名:
public interface KeyPrefix {
public int expireSeconds();
public String getPrefix();
}
接着是一个抽象的实现类实现这两个方法:
public abstract class BasePrefix implements KeyPrefix {
private int expireSeconds;
private String prefix;
public BasePrefix(int expireSeconds,String prefix)
{
this.expireSeconds=expireSeconds;
this.prefix=prefix;
}
public BasePrefix(String prefix)
{
this(0,prefix);
}
@Override
public int expireSeconds() {
return expireSeconds;
}
@Override
public String getPrefix() {
String className=getClass().getSimpleName();
return className+":"+prefix;
}
}
后面继承这个方法的自定义方法可以使用已经实现好的方法,也可以重写这两个方法
我举个例子,你可以设置一个专门存放User对象的Key的类,里面声明后一些常用的key,这样使用起来很简单明了,当然也可以使用设置过期时间和前缀名的方法来设置缓存:
像下面这个针对User对象的key类就设置了三个默认的缓存前缀,里面分别写好了前缀名和过期时间
public class UserKey extends BasePrefix {
public static final int TOKEN_EXPIRE=3600*24*2;
public static final int FORGET_TOKEN_EXPIRE=300;
public static final int CURRENT_TOKEN_EXPIRE=300;
private UserKey(int expireSeconds,String prefix)
{
super(expireSeconds,prefix);
}
public static UserKey token=new UserKey(TOKEN_EXPIRE, "tk");
public static UserKey forgettoken=new UserKey(FORGET_TOKEN_EXPIRE,"forgetTk");
public static UserKey currentToken=new UserKey(CURRENT_TOKEN_EXPIRE, "currentTk");
}
写完前缀名和过期时间相关后,还需要对redisService进行修改:
1get方法:
/**
* 获取单个对象
*/
public <T>T get(KeyPrefix prefix,String key,Class<T>clazz)
{
Jedis jedis =null;
try{
jedis=jedisPool.getResource();
String realKey=prefix.getPrefix()+key;
String str=jedis.get(realKey);
T t =stringToBean(str,clazz);
return t;
}
finally {
returnToPool(jedis);
}
}
2set方法:
/**
*设置对象
*/
public <T> boolean set(KeyPrefix prefix,String key,T value)
{
Jedis jedis =null;
try
{
jedis=jedisPool.getResource();
String realKey=prefix.getPrefix()+key;
String str=beanToString(value);
if(str==null||str.length()<=0)
{
return false;
}
int seconds=prefix.expireSeconds();
if(seconds<=0){
jedis.set(realKey,str);
}else
{
jedis.setex(realKey,seconds,str);
}
return true;
}
finally {
returnToPool(jedis);
}
}
3 exist方法:
/**
* 判断key是否存在
* */
public <T> boolean exists(KeyPrefix prefix,String key){
Jedis jedis=null;
try{
jedis=jedisPool.getResource();
String realKey=prefix.getPrefix()+key;
return jedis.exists(realKey);
}
finally {
returnToPool(jedis);
}
}
4 删除方法:
/**
* 删除
* */
public boolean delete(KeyPrefix prefix, String key) {
Jedis jedis =null;
try{
jedis=jedisPool.getResource();
String realKey=prefix.getPrefix()+key;
long res=jedis.del(realKey);
return res>0;
}
finally {
returnToPool(jedis);
}
}
5 原子自增:
/**
* 自增
* */
public <T> Long incr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
6 原子自减:
/**
* 自减
* */
public <T> Long decr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
7 对象序列化的函数:public static <T> String beanToString(T value) {
if(value == null) {
return null;
}
Class<?> clazz = value.getClass();
if(clazz == int.class || clazz == Integer.class) {
return ""+value;
}else if(clazz == String.class) {
return (String)value;
}else if(clazz == long.class || clazz == Long.class) {
return ""+value;
}else {
return JSON.toJSONString(value);
}
}
8 反序列化:
public static <T> T stringToBean(String str, Class<T> clazz) {
if(str == null || str.length() <= 0 || clazz == null) {
return null;
}
if(clazz == int.class || clazz == Integer.class) {
return (T)Integer.valueOf(str);
}else if(clazz == String.class) {
return (T)str;
}else if(clazz == long.class || clazz == Long.class) {
return (T)Long.valueOf(str);
}else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
}
9 释放Jedis对象回连接池:
private void returnToPool(Jedis jedis) {
if(jedis != null) {
jedis.close();
}
}
还有很多的一些redis操作都可以自己去按照我的这个样子去重写,我就不一一列举出来了
大家会发现这样我们在redis中存放东西很清晰明了地知道命名方式和用途,如果有不足的地方请指正