目前系统主要负责读取数据库对外提供数据接口,为了防止高并发集成了Redis和相应Redis工具类,避免缓存造轮子,封装了个注解@RedisGeneralAnnotation用以实现对该方法所有缓存读写操作。
集成Redis和封装操作Util类步骤跳过,网上一大堆就不详述了;
为了篇幅所有类中的import也省略;
1.编写Redis自定义注解@RedisGeneralAnnotation
/**
* @Description:Redis注解
* @Author: lifay
* @datetime:2019/5/28 16:32
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisGeneralAnnotation {
/**
* 缓存数据类型
*/
CacheType type();
/**
* 缓存key
*/
CacheKeys key();
/**
* 失效时间
*/
long expired() default 3600L;
/**
* 预留字段
*/
boolean read() default true;
}
Target -- 指定注解在方法上
Retention -- 指定RUNTIME运行都启用
CacheType、CacheKeys -- 很好理解,前者是缓存数据类型,也就是具体方法的返回数据类型,后者是Redis的Key,不过两者都采用枚举形式,避免重复,并且必填
expired -- 失效时间 默认3600s
2.CacheType
/**
* @Description:
* @Author: lifay
* @Return:
* @datetime:2019/5/29 15:29
*
*/
public enum CacheType {
STRING,
ENTITY,
LIST,
MAP
}
3.CacheKeys
/**
* @Description:Redis缓存Keys容器
* @Author: lifay
* @Return:
* @datetime:2019/5/29 17:04
*
*/
public enum CacheKeys {
/*获取所有用户信息*/
QUE_ALLUSER,
/*根据姓名获取用户信息*/
QUE_USER_BY_NAME
}
4.自定义注解写好了,可是光注解没用,怎么去对缓存进行实现呢,笔者选择在serviceImpl实现类进行缓存操作,所以采用AOP切面拦截serviceImpl进行缓存操作的封装,那么就新建一个用于对impl实现Redis操作的切面类(如果没有AOPjar包请先引入)
/**
* @Description:Redis切面
* @Author: lifay
* @datetime:2019/5/28 10:54
*
*/
@Aspect
@Component
public class RedisGeneralAspect {
private final Logger logger = LoggerFactory.getLogger(*.*.*.*.RedisGeneralAspect.class);//自己修改serviceImpl包路径
@Autowired
private RedisUtil redisUtil;
/**
* 定义切入点,使用了 @redisServicePoint 的方法
*/
@Pointcut("@annotation(RedisGeneralAnnotation)")
public void redisServicePoint(){
}
@Around("redisServicePoint()")
public Object redisPoint(ProceedingJoinPoint pdj){
try {
Class<?> classTarget = pdj.getTarget().getClass();
Class<?>[] pts = ((MethodSignature)pdj.getSignature()).getParameterTypes();
Method objMethod = classTarget.getDeclaredMethod(pdj.getSignature().getName(),pts);
Class<?> returnType = objMethod.getReturnType();
RedisGeneralAnnotation redisGeneralAnnotation = pdj.getTarget().getClass().getDeclaredMethod(pdj.getSignature().getName(),pts).getAnnotation(RedisGeneralAnnotation.class);
if(redisGeneralAnnotation != null && redisGeneralAnnotation.read()){
//先组装完整KEY 缓存注解的前缀key + 传参串
StringBuilder sb = new StringBuilder();
String anno_key = redisGeneralAnnotation.key().toString();
String key = "";
sb.append(anno_key);
sb.append("-");
if (pdj.getArgs() != null && pdj.getArgs().length > 0){
for(int i =0; i< pdj.getArgs().length;i++){
String arg_str = pdj.getArgs()[i].toString();
sb.append(arg_str.replace(",","^").toString());
}
}
key = sb.toString();
//进入缓存判断 Object obj = null;
if(!redisUtil.hasKey(key)){
// Redis 中不存在,则从数据库中查找,并保存到 Redis
logger.info("<====== Redis 中不存在该记录,从数据库查找 ======>");
obj = pdj.proceed();
if(obj != null) {
//查询有数据存入缓存
logger.info("-----存入redis-------");
if (redisGeneralAnnotation.type().equals(CacheType.LIST)) {
redisUtil.lSet(key,obj,redisGeneralAnnotation.expired());
} else if (redisGeneralAnnotation.type().equals(CacheType.ENTITY)) {
redisUtil.set(key,JSON.toJSONString(obj),redisGeneralAnnotation.expired());
} else if (redisGeneralAnnotation.type().equals(CacheType.MAP)) {
redisUtil.hmset(key, (Map<String, Object>) obj,redisGeneralAnnotation.expired());
} else {
redisUtil.set(key,obj,redisGeneralAnnotation.expired());
}
}
}else {
//从Redis中获取
logger.info("-----redis中获取-------");
//查询有数据存入缓存
if (redisGeneralAnnotation.type().equals(CacheType.LIST)) {
obj = redisUtil.lGet(key,0,-1);
return ((ArrayList) obj).get(0);
} else if (redisGeneralAnnotation.type().equals(CacheType.ENTITY)) {
obj = JSON.parseObject((String)redisUtil.get(key),returnType);
} else if (redisGeneralAnnotation.type().equals(CacheType.MAP)) {
obj = redisUtil.hmget(key);
} else {
obj = redisUtil.get(key);
}
}
return obj;
}else{
//方法没加缓存注解直接返回原查询方法
return pdj.proceed();
}
} catch (Throwable e){
logger.error(e.getMessage());
}
return null;
}
}
5.最后就是在impl实现类中的需要使用注解了,这个注解的原理是不管该方法的具体实现,只关注它的返回(如果没存入redis,执行原方法从DB获取到数据,再放入redis)和帮助它提前返回(从redis中直接获取数据返回)
@Override
@RedisGeneralAnnotation(type = CacheType.LIST,key = CacheKeys.QUE_ALLUSER,expired = 2)
public List<User> getAllUser() {
List<User> userList= userMapper.getAllUser();
return userList;
}