SpringAop
Spring作为沉淀了许久的框架,在开发上带给的便利性不言而喻,主要是IOC+AOP关于这这两者的介绍请自行百度。看了AOP的实现:XML实现和注解实现,个人比较倾向于注解实现,不用写过多的配置比较方便。
Redis一种缓存技术提供了丰富的数据结构,下面就利用SpringAop+Redis实现热数据查询时首先在Redis中查询,不存在时再去数据库中查询。
注解实现
package com.luckincoffee.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description 注解
* @author Kangshuai Zuo
* @date 2019/1/5 14:54
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckFromRedisAnnotation {
// Redis命名空间
String domain() ;
// Redis中的key
String key();
}
package com.luckincoffee.aspect;
import com.luckincoffee.annoation.CheckFromRedisAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
/**
* @description redis中查询切面
* @author Kangshuai Zuo
* @date 2019/1/5 14:54
*/
@Aspect
@Component
public class CheckFromRedis {
@Autowired
private RedisTemplate redisTemplate;
private static LocalVariableTableParameterNameDiscoverer parameterNameDiscover = new LocalVariableTableParameterNameDiscoverer();
@Pointcut("@annotation(com.luckincoffee.annoation.CheckFromRedisAnnotation)")
public void checkFromRedis(){}
@Around(value = "checkFromRedis()")
public Object getFromRedis(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Method method = getMethod(proceedingJoinPoint);
if(method != null){
List<CheckFromRedisAnnotation> checkFromRedisAnnotations = getCacheResultAnnotation(method);
CheckFromRedisAnnotation checkFromRedisAnnotation = checkFromRedisAnnotations.get(0);
String domain = checkFromRedisAnnotation.domain();
String key = checkFromRedisAnnotation.key();
Object keyValue = getValueByMethodParamName(proceedingJoinPoint,key);
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
Object object = hashOperations.get(domain,keyValue);
if(object != null){
return object;
}
return proceedingJoinPoint.proceed();
}
return proceedingJoinPoint.proceed();
}
/**
* 获取方法上的CheckFromRedisAnnotation注解
* @param method 方法
*/
private List<CheckFromRedisAnnotation> getCacheResultAnnotation(Method method){
Annotation[] annotations = method.getAnnotations();
List<CheckFromRedisAnnotation> checkFromRedisAnnotations = new LinkedList<>();
for(Annotation a : annotations){
if(a instanceof CheckFromRedisAnnotation){
checkFromRedisAnnotations.add((CheckFromRedisAnnotation)a);
}
}
return checkFromRedisAnnotations;
}
/**
* 获取织入切点的方法
* @param joinPoint 切点
* @return Method
*/
private Method getMethod(JoinPoint joinPoint){
// 获取切点的方法
String methodLongName = joinPoint.getSignature().toLongString();
// 反射获取类的所有方法
Method[] methods = joinPoint.getTarget().getClass().getMethods();
Method thisMethod = null;
for(Method method : methods){
if(method.toString().equals(methodLongName)){
thisMethod = method;
break;
}
}
return thisMethod;
}
/**
* 通过方法上的参数获取参数值
* @param joinPoint 切点
* @param parameterName 参数名称
* @return Object
*/
private Object getValueByMethodParamName(JoinPoint joinPoint, Object parameterName){
Method method = getMethod(joinPoint);
if (method == null){
return null;
}
String[] parameterNames = parameterNameDiscover.getParameterNames(method);
Object[] args = joinPoint.getArgs();
if(parameterNames != null && parameterName != null){
int i;
for(i=0; i<parameterNames.length; ++i){
if(parameterName.equals(parameterNames[i])){
return args[i];
}
}
}
return null;
}
}
注解使用
/**
* 获取商品
* @param goodsId 商品ID
* @return Goods
*/
@Override
@CheckFromRedisAnnotation(domain = "goods",key = "goodsId")
public Goods getGoodsByGoodsId(String goodsId) {
try{
return goodsMapper.getByGoodsId(goodsId);
}catch (Exception e){
LOG.info("获取商品失败:"+e);
return null;
}
}
在方法上面加入注解@CheckFromRedisAnnotation(domain = "goods",key = "goodsId"),该注解在执行的时候会先去Redis中查询命名空间和主键分别为goods和goodsId的商品信息,商品中有会直接返回商品信息,没有查询到再去数据库中查询然后存入Redis。可以减轻数据库的压力提高查询性能,但是如果该商品的信息被删除或者被修改也一定要删除Redis中该商品的信息,否则会造成数据的不一致性。