《AOP实现Redis缓存业务》

AOP实现缓存业务

业务需求

  • 1 使用自定义注解来表示业务方法,业务的返回值保存到缓存中

  • 2 利用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. 注解表示业务方法

在这里插入图片描述

  • 这里的是我们的自定义注解

  • key 是我们把业务方法起个名字

  • 在我们的自定义注解描述的方法在AOP中拦截

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缓存");
}
  • 判断redis缓存中是否存在当前我们封装的KEY

  • 如果存在的话 get来获取KEY的值

String json=jedis.get(Key);
  • joinPointm.getSignature() 这个方法来获取目标方法的签名

    • 我们要赋值这里使用道理joinPointm的父类MethodSignature
    • 打印的结果 显示的就是目标方法的签名

在这里插入图片描述

 MethodSignature methodSignature= (MethodSignature) joinPointm.getSignature();
  • 根据签名动态获取目标方法的类型

  • getReturnType 获取当前目标方法签名的类型(就是目标方法的类型)

  • 打印的结果 显示的是 目标方法的类型

在这里插入图片描述

 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();
    }
}
  • 如果redis中没有查询的数据,那么直接执行目标方法,在数据库中获取

  • joinPointm.proceed(); 执行目标方法

result =joinPointm.proceed(); 
  • 目标方法查询数据库的返回值 转换成Json类型

String json=RedisUtl.toJSON(result);
  • 目前我们没有设定超时时间,将来我们超时时间的话,那就超时的时间大于0 那么存储的时候定义我们设置的超时时间

  • 如果没有超时的话直接存储

if (redisAopFind.seconds()>0){
    
    
    jedis.setex(Key, redisAopFind.seconds(), json);
}else {
    
    
    jedis.set(Key, json);
}

扩展

我们定义自定义注解的时候,定义了默认不超时,那么定义超时时间的话
在这里插入图片描述
在自定义注解描述方法的时候 加上超时时间


3.业务完成

return result - 返回客户端查询的结果
在这里插入图片描述
第一次查询的是数据库
第二次查询的是缓存

猜你喜欢

转载自blog.csdn.net/weixin_45103228/article/details/113773727