乐趣区

关于aop:缓存服务器Redis-04-AOP实现Redis缓存服务

在上文中, 咱们尽管在业务层 service 中实现了代码的实现. 然而该代码不具备复用性. 如果换了其余的业务则须要从新编辑.
并且因为缓存的代码写在业务层 service 中, 所以代码的耦合性高, 不不便当前的扩大.
所以咱们从代码复用以及升高代码的耦合性的方面应用 AOP 来实现 redis 缓存.

AOP

AOP 即面向切面编程 – 在不改变原有代码的根底上, 对业务进行加强.
AOP = 切入点表达式 + 告诉办法

专业术语:
1. 连接点: 在执行失常的业务过程中满足了切入点表达式时进入切面的点.(织入) 多个
2. 告诉: 在切面中执行的具体的业务(扩大) 办法
3. 切入点: 可能进入切面的一个判断 if 判断 一个
4. 指标办法: 将要执行的实在的业务逻辑.

告诉办法

分类:
1. 前置告诉 (@Before): 指标办法执行之前执行
2. 后置告诉(@After): 指标办法执行之后执行
3. 异样告诉(@AfterThrowing): 指标办法执行之后抛出异样时执行
4. 最终告诉(@AfterReturning): 不论什么时候都要执行的办法.
阐明: 上述的四大告诉类型 不能控制目标办法是否执行 . 个别应用上述的四大告诉类型, 都是用来记录程序的 执行状态 .
5. 盘绕告诉 (@Around): 在指标办法执行前后都要执行的告诉办法 . 控制目标办法是否执行. 并且盘绕告诉的性能最为弱小.
盘绕告诉是最为罕用的.

切入点表达式

分类:
1.bean(bean 的 id) 类名首字母小写 匹配 1 个类
2.within(包名. 类名) 按包门路匹配类 匹配多个类
上述表达式是粗粒度的管制, 按类匹配.
3.execution(返回值类型 包名. 类名. 办法名 (参数列表))
4.@annotation(包名. 注解名) 按注解进行拦挡.
上述是细粒度的管制.
bean 和 @annotation 较为罕用

实现 Redis 缓存

业务剖析

1. 自定义注解 CacheFind 次要被注解标识的办法, 则开启缓存的实现.
2. 为了未来辨别业务, 须要在注解中标识 key 属性, 由使用者自行填写.
3. 为了用户提供数据超时性能.

自定义注解 @CacheFind

@Retention(RetentionPolicy.RUNTIME) // 该注解运行时无效
@Target({ElementType.METHOD})       // 对办法无效
public @interface CacheFind {String key();               // 该属性为必须增加
    int seconds() default 0;    // 设定超时工夫 默认不超时}

编写 CacheAOP 类

package com.jt.aop;  
  
import com.jt.annotation.CacheFind;  
import com.jt.util.ObjectMapperUtil;  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.Signature;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
import org.aspectj.lang.reflect.MethodSignature;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
import org.springframework.util.StringUtils;  
import redis.clients.jedis.Jedis;  
  
import java.util.Arrays;  
  
@Component  
@Aspect  
public class RedisAOP {  
 // 注入缓存 reids 对象  
 @Autowired  
 private Jedis jedis;  
 
 //@Pointcut("@annotation(com.jt.annotation.CacheFind)")  
 //public void doRedisPointCut(){}  
 /**  
 * 拦挡 @CacheFind 注解示意的办法  
 * 告诉抉择: 缓存的实现应该选用盘绕告诉  
 * 步骤:  
 *  1. 动静生成 key 用户填写的 key+ 用户提交的参数  
 */  
 @Around("@annotation(cacheFind)")  
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){  
        try {  
            Object result = null;  
 //1. 如何动静获取用户在注解中填写的数据  
 String prekey = cacheFind.key();  
 //2. 动静获取指标办法中的参数  将数组转化为字符串  
 String args = Arrays.toString(joinPoint.getArgs());  
 String key = prekey + "::" + args; //"ITEM_CAT_PARENTID::[0]"  
 //3. 查问缓存中查问  
 //4. 判断 json 中是否有值  
 if (jedis.exists(key)) {  // 先获取 json 数据  
 String json = jedis.get(key);  
 // 动静获取指标办法的返回值类型?? 向上造型 不必强转   向下造型  
 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();  
 Class target = methodSignature.getReturnType();  
 //7. 进入代表程序中有值, 将 json 转为对象  
 result = ObjectMapperUtil.toObject(json, target);  
 System.out.println("AOP 实现缓存查问!!!!");  
 }else {  
                //5. 若缓存中没有查询数据库  
 result = joinPoint.proceed(); // 执行指标办法, 获取返回值后果  
 System.out.println("AOP 执行数据库操作");  
 //6. 将数据存到 redis 中  
 // 先转为 json 格局  
 String json = ObjectMapperUtil.toJSON(result);  
 if(cacheFind.seconds()>0){  // 判断是否须要超时工夫  
 jedis.setex(key, cacheFind.seconds(), json);  
 }else {jedis.set(key, json);  
 }  
            }  
            return result;  
 }catch (Throwable e){e.printStackTrace();  
 throw new RuntimeException(e);  
 }  
    }  
}

留神

对于盘绕告诉参数的阐明:
1. 连接点必须位于告诉办法参数的第一位
2. 其余四大告诉类型不能够增加 ProceedingJoinPoint 对象, 能够增加 JointPoint 对象

JoinPoint 应用

 /**
     * 要求: 拦挡注解办法
     * 打印:
     *      1. 打印指标对象的类型
     *      2. 打印办法的参数
     *      3. 获取指标对象的名称及办法的名称
     * @param joinPoint
     */
    @Before("@annotation(com.jt.anno.CacheFind)")
    public void before(JoinPoint joinPoint){Object target = joinPoint.getTarget();  // 获取指标对象
        Object[] args = joinPoint.getArgs();    // 获取办法参数的
        String targetName =
                joinPoint.getSignature().getDeclaringTypeName(); // 获取指标对象的名称
        // 获取指标对象的类型
        Class targetClass = joinPoint.getSignature().getDeclaringType();
        // 获取指标办法的名称
        String methodName = joinPoint.getSignature().getName();
        System.out.println(target);
        System.out.println(args);
        System.out.println(targetName);
        System.out.println(targetClass);
        System.out.println(methodName);

    }
退出移动版