AOP实现缓存业务
业务需求
提前准备
1. 创建Redis的配置文件(准备redis节点)
创建properties配置文件,里面存入Redis节点
#准备redis的节点信息
redis.host=192.168.126.129
redis.port=6379
2.创建Redis配置类(节点交给spring容器进行管理)
创建class类 里面导入配置文件存储的节点信息并且交给spring管理
@Configuration //表示一个配置类 一一般会@bean的注解关联
@PropertySource("classpath:/redis.properties") //导入配置文件
public class redis {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean //将方法的返回值结果,交给spring容器进行管理.
public Jedis jedis(){
return new Jedis(host, port);
}
}
3.创建Redis的工具类(用来JSON格式转换对象并且对象转换JSON)
把数据库的对象格式转换JSON格式存储缓存中,把缓存的JSON格式转换对象格式传入客户端
public class RedisUtl {
private static final ObjectMapper MAPPER=new ObjectMapper();
//对象转json
public static String toJSON(Object object) {
try {
return MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
//2.JSON转化为对象 要求用户传递什么类型就返回什么对象??
public static <T> T toObject(String json,Class<T> target){
try {
return MAPPER.readValue(json, target);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
初始案例
1.创建自定义注解
注解是接口,用来指定目标业务方法,并且拦截
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisAopFind {
String key();
int seconds() default -1;
}
使用的注解
@Target() ----- 此注解描述注解的使用范围,此注解使用什么地方
ElementType ----- 取值
METHOD ----- 此注解用于描述方法
@Retention() ----- 作用是定义被它所注解的注解保留多久
RetentionPolicy ----- 取值
RUNTIME ----- 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
String key(); ---- 定义key值
int seconds() default -1; ---- 超时时间 default -1 是 默认不超时
2. 注解表示业务方法
3.初始AOP
3.1 创建Java类,表示创建类是Aop的业务类
当前的类是处理AOP业务的类
@Component
@Aspect
public class RedisAOP {
@Autowired
private Jedis jedis;
}
@Component ----- 此注解描述类交给spring管理
@Aspect ----- 此注解描述的类是表示AOP(面向切面)的类
@Autowired
private Jedis jedis;
我们导入Redis的配置类
4.业务结构
@Component
@Aspect
public class RedisAOP {
@Autowired
private Jedis jedis;
@Around("@annotation(redisAopFind)")
public Object around(ProceedingJoinPoint joinPointm, RedisAopFind redisAopFind){
Object result=null;
String Key=redisAopFind.key();
//动态获取方法的参数
String args= Arrays.toString(joinPointm.getArgs());
Key = Key + "::" + args;
if (jedis.exists(Key)){
String json=jedis.get(Key);
//动态获取方法的类型
MethodSignature methodSignature= (MethodSignature) joinPointm.getSignature();
Class returnType = methodSignature.getReturnType();
result = RedisUtl.toObject(json, returnType);
System.out.println("AOP查询redis缓存");
}else{
try {
result =joinPointm.proceed(); //执行下一个通知,执行目标方法
String json=RedisUtl.toJSON(result);
if (redisAopFind.seconds()>0){
jedis.setex(Key, redisAopFind.seconds(), json);
}else {
jedis.set(Key, json);
}
System.out.println("AOP查询数据库");
}catch (Throwable throwable) {
throwable.printStackTrace();
}
}
return result;
}
}
第一步 拦截业务并且封装KEY
@Around("@annotation(redisAopFind)")
public Object around(ProceedingJoinPoint joinPoint, RedisAopFind redisAopFind){
Object result=null;
String Key=redisAopFind.key();
//动态获取方法的参数
String args= Arrays.toString(joinPointm.getArgs());
Key = Key + "::" + args;
首先我导入我们的自定义注解 @Around是环绕通知,我们用环绕通知来拦截我们自定义注解描述的方法,以及获取方法的返回值
- JoinPoint – 切入点方法,正在执行的目标方法信息的对象,通俗点(我们拦截目标方法的管理者)
- redisAopFind – 我们创建的自定义注解
- redisAopFind.key() – 获取自定义注解中的key ,注解描述目标方法的时候我们赋值给的key值
- joinPointm.getArgs() – 获取目标方法中的参数,我们需要字符串拼接所以使用tostring方法
Key = Key + "::" + args;
- 获取的key值和目标方法的参数拼接,封装redis中的KEY值
提示:
我们的KEY是有变化的,不能使用固定key,所以参数来拼接的话,客户端请求的参数不一样可以,我们的key值也有动态的变化,这样很容易分别,存储的时候也是对应的参数来封装
第二步 校验
1- Redis中存在KEY的值(获取redis中的)
if (jedis.exists(Key)){
String json=jedis.get(Key);
//动态获取方法的类型
MethodSignature methodSignature= (MethodSignature) joinPointm.getSignature();
Class returnType = methodSignature.getReturnType();
result = RedisUtl.toObject(json, returnType);
System.out.println("AOP查询redis缓存");
}
String json=jedis.get(Key);
MethodSignature methodSignature= (MethodSignature) joinPointm.getSignature();
Class returnType = methodSignature.getReturnType();
缓存中KEY的值转换成目标方法的类型,并返回
result = RedisUtl.toObject(json, returnType);
2- redis中不存在KEY的值(查询数据库并且存储缓存中)
else{
try {
result =joinPointm.proceed(); //执行下一个通知,执行目标方法
String json=RedisUtl.toJSON(result);
if (redisAopFind.seconds()>0){
jedis.setex(Key, redisAopFind.seconds(), json);
}else {
jedis.set(Key, json);
}
System.out.println("AOP查询数据库");
}catch (Throwable throwable) {
throwable.printStackTrace();
}
}
result =joinPointm.proceed();
String json=RedisUtl.toJSON(result);
if (redisAopFind.seconds()>0){
jedis.setex(Key, redisAopFind.seconds(), json);
}else {
jedis.set(Key, json);
}
扩展
我们定义自定义注解的时候,定义了默认不超时,那么定义超时时间的话
在自定义注解描述方法的时候 加上超时时间
3.业务完成
return result - 返回客户端查询的结果
第一次查询的是数据库
第二次查询的是缓存