import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
@Component
@Aspect
public class MyRedisCacheAspect
{
@Resource
private RedisTemplate redisTemplate;
//配置织入点
@Pointcut("@annotation(com.zzyy.study.annotations.MyRedisCache)")
public void cachePointCut(){}
/**
*
* @param joinPoint
* @return
*/
@Around("cachePointCut()")
public Object doCache(ProceedingJoinPoint joinPoint)
{
Object result = null;
try
{
//1 获得重载后的方法名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass()
.getMethod(signature.getName(),signature.getMethod().getParameterTypes());
//2 确定方法名后获得该方法上面配置的注解标签MyRedisCache
MyRedisCache myRedisCacheAnnotation = method.getAnnotation(MyRedisCache.class);
//3 拿到了MyRedisCache这个注解标签,获得该注解上面配置的参数进行封装和调用
String keyPrefix = myRedisCacheAnnotation.keyPrefix();
String matchValueSpringEL = myRedisCacheAnnotation.matchValue();
//4 SpringEL 解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(matchValueSpringEL);
EvaluationContext context = new StandardEvaluationContext();
//5 获得方法里面的形参个数
Object[] args = joinPoint.getArgs(); //Integer userId 17
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i],args[i].toString());
}
//6 通过上述,拼接redis的最终key形式,redis的key 等于 keyPrefix::matchValue
String key = keyPrefix+"::"+expression.getValue(context).toString();
//7 先去redis里面查询看有没有
result = redisTemplate.opsForValue().get(key);
if(result != null)
{
return result;
}
//8 redis里面没有,去找msyql查询或叫进行后续业务逻辑
//-------aop精华部分,才去找findUserById方法干活
result = joinPoint.proceed();
//9 mysql步骤结束,还需要把结果存入redis一次,缓存补偿
redisTemplate.opsForValue().set(key,result);
}catch (Throwable throwable){
throwable.printStackTrace();
}
return result;
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRedisCache
{
//SpringEL表达式,解析占位符对应的匹配value值
String matchValue();
//约等于键的前缀prefix,
String keyPrefix();
}
@MyRedisCache(keyPrefix = "userv3",matchValue = "#userId") //redis的key 等于 keyPrefix::matchValue
public User findUserById(Integer userId)
{
log.info("=======执行了数据库mysql的查询V3====: "+userId);
return userMapper.selectByPrimaryKey(userId);
}