乐趣区

关于aop:AOP形式实现Redis缓存

AOP

连接点罕用 API

ProceedingJoinPoint 只能盘绕告诉应用(ProceedingJoinPoint 控制目标办法的执行),如果当做参数来用,必须位于参数的第一位

private void saveSysLog(ProceedingJoinPoint point){
 //1. 获取指标类(就相当于晓得了是哪一个类)Class<?> targetCls = point.getTarget().getClass();
 MethodSignature ms= (MethodSignature) point.getSignature();
 System.out.println("ms________________"+ms);
 String methodName = ms.getName();// 再获取办法名
 //2. 获取类里的办法(类里有很多办法 通过办法名和办法参数类型可能确定是哪一个办法)Method targetMethod=targetCls.getMethod(methodName,ms.getParameterTypes());
 //3. 获取指标办法上的注解
 RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);
 //4. 获取注解上的值
if(requiredLog!=null){operation=requiredLog.value();
}
String method=targetCls.getName()+"."+methodName;
 Object[] paramsObj=point.getArgs();
 // 转一般串
 //String params= Arrays.toString(paramsObj);
 // 将参数转换为字符串
 String params=new ObjectMapper().writeValueAsString(paramsObj);
 }
public void before(JoinPoint joinPoint){
 //getSignature:获取是一个对象(外面蕴含办法的返回值 办法名 办法参数)String methodName = joinPoint.getSignature().getName();
 String typeName = joinPoint.getSignature().getDeclaringTypeName();
 System.out.println("指标办法的门路:"+typeName+"."+methodName);
 // getArgs:返回指标办法的参数
 Object[] args = joinPoint.getArgs();
 System.out.println("参数:"+ Arrays.toString(args));
 Class<?> methodClass = joinPoint.getTarget().getClass();
 System.out.println("指标对象类型:"+methodClass);

入门案例

@Aspect // 我是一个 aop 的切面类
@Component// 将类交给 spring 容器治理
public class CacheAOP {
 // 公式 = 切入点表达式 + 告诉办法
 /*
 * 对于切入点表达式的应用阐明
 * 粗粒度:*   1.bean(bean 的 Id)一个类 bean 的 Id 指的是交给 spring 容易治理的类的类名小写,* 也能够 bean(*ServiceImpl) 多个类
 *   2.within(包名. 类名) 一个类 /* 代替就是多个
 */ 
 @Pointcut("bean(itemCatServiceImpl)")
 public void pointCut(){// 定义切入点表达式 只为了占位}
 // 定义前置告诉,与切入点表达式进行绑定,留神绑定的是办法
 @Before("pointCut()")
 // 等同于 @Before("bean(itemCatServiceImpl") 区别:pointCut()示意切入点表达式的援用
 // 实用于多个告诉同用状况
 public void before(JoinPoint joinPoint){
 //getSignature:获取是一个对象(外面蕴含办法的返回值 办法名 办法参数)String methodName = joinPoint.getSignature().getName();
 String typeName = joinPoint.getSignature().getDeclaringTypeName();
 System.out.println("指标办法的门路:"+typeName+"."+methodName);
 // getArgs:返回指标办法的参数
 Object[] args = joinPoint.getArgs();
 System.out.println("参数:"+ Arrays.toString(args));
 Class<?> methodClass = joinPoint.getTarget().getClass();
 System.out.println("指标对象类型:"+methodClass);
 System.out.println("我是前置告诉");
 }
 
 @Around("pointCut()")
public Object doaround(ProceedingJoinPoint joinPoint) {
 /**
 * ProceedingJoinPoint 只能盘绕告诉应用(ProceedingJoinPoint 控制目标办法的执行),* 如果当做参数来用,必须位于参数的第一位,*/
 Object result=null;
 try {result = joinPoint.proceed();// 执行下一个告诉或指标办法
 } catch (Throwable throwable) {throwable.printStackTrace();
 }
 System.out.println("盘绕告诉完结");
 return result;
}
 
 }

AOP 实现 Redis 缓存

编写配置类

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {@Value("${redis.host}")
 private String host;
 @Value("${redis.port}")
 private Integer port;
 @Bean
 public Jedis jedis(){return new Jedis(host,port);
 }
}

编写工具类

public class ObjectMapperUtil {private static final ObjectMapper mapper=new ObjectMapper();
 //1. 将用户传递的数据转化为 json
 public static String toJson(Object object){if(object==null) {throw  new RuntimeException("传递的数据为 null 请查看");
 }
 try {String json = mapper.writeValueAsString(object);
 return json ;
 } catch (JsonProcessingException e) {e.printStackTrace();
 throw new RuntimeException(e);
 }
 }
 // 要求用户传递什么样的类型,就返回什么样的对象
 public static <T> T toObject(String json,Class<T> targer){if (StringUtils.isEmpty(json)||targer==null) {throw new RuntimeException("参数不能为空");
 }
 try {return mapper.readValue(json, targer);
 } catch (JsonProcessingException e) {e.printStackTrace();
 throw new RuntimeException(e);
 }
 }}

自定义缓存注解

如何管制 哪些办法须要应用缓存?
解决方案:采纳自定义注解的模式 进行定义 如果办法执行须要应用缓存,则标识
对于注解的阐明:

 1. 注解名称:2. 属性参数:key: 应该由用户本人手动增加 个别增加业务名称 之后动静拼接造成惟一的
      seconds:用户能够指定数据的超时工夫
      
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFind {public String preKey();// 用户标识 key 的前缀
 public int seconds() default 0;// 如果用户不写示意不须要超时,如果写了以用户为准}

开始应用注解

实现代码

@Aspect // 我是一个 aop 的切面类
@Component// 将类交给 spring 容器治理
public class CacheAOP {
 @Autowired
 private Jedis jedis;
 /**
 * 1. 动静生成 key preKey+ 用户参数数组
 * @param joinPoint
 * @return
 */
 @Around("@annotation(cacheFind)")
 public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){System.out.println("注解拦挡");
 Object result=null;
 try {
 //1. 拼接 Redis 存储数据的 key
 Object[] args = joinPoint.getArgs();
 String key = cacheFind.preKey()+"::" + Arrays.toString(args);
 //2. 查问 redis
 if(jedis.exists(key)){
 //redis 中有记录
 String json=jedis.get(key);
 // 将数据转化成须要的类型——办法的返回值类型
 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
 Class type = signature.getReturnType();
 result = ObjectMapperUtil.toObject(json,type);
 }else{
 // 不存在 查询数据库
 result=joinPoint.proceed();// 执行指标办法
 // 将查问后果保留到 Redis 中
 String json= ObjectMapperUtil.toJson(result);
 // 判断数据是否须要超时工夫
 if(cacheFind.seconds()>0){jedis.setex(key,cacheFind.seconds(),json);
 }else {jedis.set(key,json);
 }
 }
 } catch (Throwable throwable) {throwable.printStackTrace();
 }
 return result;
 }
}
退出移动版