AOP实现Redis缓存

小飞 2020-11-09 22:19:23
redis 缓存 实现 AOP


AOP简单介绍

名称: 面向切面编程
作用: 降低系统中代码的耦合性,并且在不改变原有代码的条件下对原有的方法进行功能的扩展.
公式: AOP = 切入点表达式 + 通知方法

通知类型

1.前置通知 目标方法执行之前执行
2.后置通知 目标方法执行之后执行
3.异常通知 目标方法执行过程中抛出异常时执行
4.最终通知 无论什么时候都要执行的通知

特点: 上述的四大通知类型 不能干预目标方法是否执行.一般用来做程序运行状态的记录.【监控】

5.环绕通知 在目标方法执行前后都要执行的通知方法 该方法可以控制目标方法是否运行.joinPoint.proceed(); 功能作为强大的.

切入点表达式

切入点表达式就是一个程序是否进入通知的一个判断(IF)。
作用:当程序运行过程中,满足了切入点表达式时才回去执行通知方法,实现业务的扩展。
种类(写法):
1.bean(bean的名称 bean的ID) 只能拦截具体的某个bean对象,只能匹配一个对象。
例如:bean("itemServiceImpl")
2.within(包名.类名) within("com.jt.service.*")表示可以匹配多个对象。
3.execution(返回值类型 包名.类名.方法名(参数列表))最为强大的用法
例如 : execution( com.jt.service...*(..))返回值类型任意 com.jt.service包下的所有的类的所有的方法都会被拦截.
4.@annotation(包名.注解名称) 按照注解匹配.

AOP入门案例

package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //我是一个AOP切面类
@Component //将类交给spring容器管理
public class CacheAOP {
//公式: 切入点表达式+通知方法
/**
* 关于切入点表达式的使用说明
* 粗粒度
* 1.bean(bean的id) 一个类
* 2.within(包名.类名) 多个类
* 细粒度
* 1.
*/
//@Pointcut("bean(itemCatSercviceImpl)")
// .*表示当前包下面的一级目录。
// ..*表示当前包下面的多级目录
/*@Pointcut("within(com.jt.service..*)")
public void pointCut(){
//定义切入点表达式, 为了占位
}*/
//定义前置通知,与切入点进行绑定 @Before("pointCut()")
//或者用更简单的方法:不用定义切入点方法pointCut(),
// 直接绑定bean:@before(“bean(itemCatSercviceImpl)”)
/*两者的区别:
* 1.@Before("pointCut()")表示切入点表达式的引用,适用于多个通知同用切入点的情况
* 2.@before(“bean(itemCatSercviceImpl)”)适用于单个通知,不需要复用的
* */
/**@Before("pointCut()")
public void before(){
System.out.println("我是前置通知");
}
@AfterReturning("pointCut()")
public void afterreturn(){
System.out.println("我是后置通知");
}*/
@Before("pointCut()")
public void before(JoinPoint joinPoint){
//JoinPoint:连接点方法
//getStaticPart()代表连接点的方法签名
//getName()方法的名称
String methodName = joinPoint.getSignature().getName();
//getDeclaringTypeName()获取类的路径全名com.jt.service.ItemCatSercviceImpl
String className = joinPoint.getSignature().getDeclaringTypeName();
System.out.println(className+"."+methodName);
//getTarget()代表目标对象
//getTarget().getClass()获取目标对象的类型
Class<?> targetClass = joinPoint.getTarget().getClass();
//获取对象的参数
Object[] args = joinPoint.getArgs();
//获取执行时间
Long startTime= System.currentTimeMillis();
System.out.println("目标方法类型"+targetClass);
System.out.println("执行时间:"+startTime+"毫秒");
}
/**
* 环绕通知说明
* 注意事项:
* ProceedingJoinPoint只能用在环绕通知里
* 1.环绕通知必须添加参数ProceedingJoinPoint
* 2.ProceedingJoinPoint只能环绕通知使用
* 3.ProceedingJoinPoint如果当做参数则必须位于参数的第一位
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("环绕通知开始!!!");
Object result=null;
try {
result=joinPoint.proceed();//执行下一个通知或者目标方法
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束!!!");
return result;
}
}

关于AOP实现Redis

自定义缓存注解

问题:如何控制,哪些方法需要使用缓存?【cacheFind()查询方法】
解决方案:采用自定义注解的形式进行定义,如果方法执行需要使用缓存,则标识注解即可。
关于注解的说明:
1.注解名称:cacheFind
2.属性参数:

1.key:应该由用户自己手动添加,一般添加业务名称之后动态拼接形成唯一的key。
2.seconds:用户可以指定数据的超时时间。

image.png

//自定义一个注解
@Target(ElementType.METHOD)//该注解只对方法有效
@Retention(RetentionPolicy.RUNTIME)//运行期有效
public @interface CacheFind {
String preKey();//用户标识key的前缀。
int seconds() default 0;//如果用户不写表示不需要超时,如果写了就以用户为准。
}

image.png

编辑CacheAOP

package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Arrays;
@Aspect //我是一个AOP切面类
@Component //将类交给spring容器管理
public class CacheAOP {
@Autowired
private Jedis jedis;
/** AOP实现Redis缓存
* 切面=切入点+通知方法
* 注解相关+环绕通知 控制目标方法是否执行
*
* 难点:
* 1.获取注解的对象,将注解作为参数传过来
* 2.动态生成key prekey+用户参数数组
* 3.如何获取方法的返回值类型
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
System.out.println("注解拦截生效");
Object result=null;
try {
//拼接redis存储数据的key
Object[] args=joinPoint.getArgs();
String key=cacheFind.preKey()+"::"+ Arrays.toString(args);
//查询redis 之后判断是否有数据
if(jedis.exists(key)){
//表示redis中有数据,无需执行目标方法
String json = jedis.get(key);
//动态获取方法的返回值类型 向上转型 向下转型
MethodSignature methodSignature=(MethodSignature)joinPoint
.getSignature();
//表示客户端传来什么类型就返回什么类型
Class returnType = methodSignature.getReturnType();
//需要将json串转化为对象
result=ObjectMapperUtil.toObj(json, returnType);
System.out.println("redis缓存中的数据");
}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);
}
System.out.println("aop执行目标方法查询数据库");
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
版权声明
本文为[小飞]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000037779017

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云