一般缓存与数据库的配合使用是这样的。

1.查询缓存中是否有数据。

2.缓存中无数据,查询数据库。

3.把数据库数据插入到缓存中。

  其实我们发现 1,3 都是固定的套路,只有2 是真正的业务代码。我们可以把1,3 抽取出来,封装到一个自定义注解@myCache 上,通过给2方法加一个注解,实现代码的解耦。

  1. package com.itbac.common.cache;
  2.  
  3. import org.springframework.stereotype.Service;
  4.  
  5. @Service
  6. public class SkuQueryService {
  7.    //注解的使用
  8. @myCache(key = "'SkuQueryService_findById' + #id")
  9. public Object findById(String id){
  10.  
  11. System.out.println("findById方法查询数据库");
  12.  
  13. return "数据库返回值:"+id;
  14. }
  15. }

自定义注解

  1. package com.itbac.common.cache;
  2.  
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7.  
  8. // 注解的生命周期:运行时
  9. @Retention(RetentionPolicy.RUNTIME)
  10. // 注解的应用范围:修饰方法
  11. @Target(ElementType.METHOD)
  12. public @interface myCache {
  13. /**
  14. * key 的生成规则,通过springEL表达式
  15. * @return
  16. */
  17. String key();
  18. }

AOP切面类,切面编程,动态解析注解。

  1. package com.itbac.common.cache;
  2.  
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.Around;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.core.DefaultParameterNameDiscoverer;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.expression.Expression;
  11. import org.springframework.expression.spel.standard.SpelExpressionParser;
  12. import org.springframework.expression.spel.support.StandardEvaluationContext;
  13. import org.springframework.stereotype.Component;
  14.  
  15. import java.lang.reflect.Method;
  16. import java.util.concurrent.TimeUnit;
  17.  
  18. /**
  19. * Aop切面编程
  20. */
  21. @Component
  22. @Aspect
  23. public class cacheAOP {
  24.  
  25. @Autowired
  26. private RedisTemplate redisTemplate;
  27.   
      
       // 环绕通知 : 监控自定义注解 。 
  28. @Around("@annotation(com.itbac.common.cache.myCache)")
  29. public Object doAnyThing(ProceedingJoinPoint joinPoint) throws Throwable {
  30. String key =null;
  31. //1.反射技术:从注解里里面,读取key的生成规则。
  32. //1.1 从切入点,获取方法签名
  33. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  34. //1.2 从切点,获取目标对象的字节码,的方法。参数:(方法名,方法的所有参数)。
  35. Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
  36. //1.3 从方法获取注解。
  37. myCache annotation = method.getAnnotation(myCache.class);
  38. //1.4 从注解,获取注解信息。'SkuQueryService_findById' + #id
  39. String keyEL = annotation.key();
  40.  
  41. //2. 创建 springEL表达式 解析器
  42. SpelExpressionParser parser = new SpelExpressionParser();
  43. // 解析器 获取指定表达式 'SkuQueryService_findById' + #id 的表达式对象
  44. Expression expression = parser.parseExpression(keyEL);
  45. // 设置解析上下文
  46. StandardEvaluationContext context = new StandardEvaluationContext();
  47. //2.1 创建默认参数名 发现者
  48. DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
  49. //2.2 获取方法中的所有参数名。
  50. String[] parameterNames = discoverer.getParameterNames(method);
  51. //2.3 获取切点方法中的所有参数值。
  52. Object[] args = joinPoint.getArgs();
  53. for (int i = 0; i < parameterNames.length; i++) {
  54. // 把参数名,参数值,设置到解析器上下文
  55. context.setVariable(parameterNames[i],args[i].toString());
  56. }
  57. //表达式 匹配 解析上下文 中的内容 ,拿到key
  58. key = expression.getValue(context).toString();
  59.  
  60. Object o = redisTemplate.opsForValue().get(key);
  61. if (o !=null){
  62. // 缓存中有数据,直接返回。
  63. //查询缓存
  64. System.out.println("查询缓存返回。");
  65. //延迟缓存失效时间,1天。
  66. redisTemplate.expire(key,1,TimeUnit.DAYS);
  67. return o;
  68. }
  69. // 缓存穿透标记
  70. Object penetrateFlag = redisTemplate.opsForValue().get(key + "penetrateFlag");
  71. if (null == penetrateFlag){
  72. // 没有防止 缓存穿透标记,查数据库。
  73.  
  74. // 执行切点 中的代码,查询数据库。
  75. Object proceed = joinPoint.proceed();
  76.  
  77. if (null == proceed){
  78. // 数据库数据为空,设置缓存穿透标记。15分钟
  79. redisTemplate.opsForValue().set(key + "penetrateFlag",true,15,TimeUnit.MINUTES);
  80. }else {
  81. // 数据库数据不为空,把数据存到缓存中。缓存1天。
  82. redisTemplate.opsForValue().set(key,proceed,1, TimeUnit.DAYS);
  83. }
  84. return proceed ;
  85. }
  86. // 延迟缓存失效时间 15分钟 缓存穿透标记
  87. redisTemplate.expire(key+"penetrateFlag",15,TimeUnit.MINUTES);
  88. // 返回
  89. return null;
  90. }
  91. }

这样就可以通过一个自定义注解@myCache ,实现了缓存与业务代码的解耦。其中还包含了防止缓存穿透的使用技巧。不要告诉别人哦。

注解与AOP切面编程实现redis缓存与数据库查询的解耦的更多相关文章

  1. SpringBoot 通过自定义注解实现AOP切面编程实例

    一直心心念的想写一篇关于AOP切面实例的博文,拖更了许久之后,今天终于着手下笔将其完成. 基础概念 1.切面(Aspect) 首先要理解‘切’字,需要把对象想象成一个立方体,传统的面向对象变成思维,类 ...

  2. 注解配置AOP切面编程

    1.导入先关jar包 2.编写applicationContext.xml,配置开启注解扫描和切面注解扫描 <?xml version="1.0" encoding=&quo ...

  3. 基于SpringBoot AOP面向切面编程实现Redis分布式锁

    基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...

  4. AOP切面编程在android上的应用

    代码地址如下:http://www.demodashi.com/demo/12563.html 前言 切面编程一直是一个热点的话题,这篇文章讲讲一个第三方aop库在android上的应用.第三方AOP ...

  5. SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务

    本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.AOP切面编程 1.什么是AOP编程 在软件业,AOP为Asp ...

  6. 十:SpringBoot-配置AOP切面编程,解决日志记录业务

    SpringBoot-配置AOP切面编程,解决日志记录业务 1.AOP切面编程 1.1 AOP编程特点 1.2 AOP中术语和图解 2.SpringBoot整合AOP 2.1 核心依赖 2.2 编写日 ...

  7. Spring MVC通过AOP切面编程 来拦截controller 实现日志的写入

    首选需要参考的是:[参考]http://www.cnblogs.com/guokai870510826/p/5977948.html    http://www.cnblogs.com/guokai8 ...

  8. Spring AOP 切面编程记录日志和接口执行时间

    最近客户现在提出系统访问非常慢,需要优化提升访问速度,在排查了nginx.tomcat内存和服务器负载之后,判断是数据库查询速度慢,进一步排查发现是因为部分视图和表查询特别慢导致了整个系统的响应时间特 ...

  9. 本地缓存,Redis缓存,数据库DB查询(结合代码分析)

    问题背景 为什么要使用缓存?本地缓存/Redis缓存/数据库查询优先级? 一.为什么要使用缓存 原因:CPU的速度远远高于磁盘IO的速度问题:很多信息存在数据库当中的,每次查询数据库就是一次IO操作所 ...

随机推荐

  1. 设计模式之策略模式和状态模式(strategy pattern & state pattern)

    本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...

  2. 为什么现在这么多人开始学习Python?

    近几年Python编程语言在国内引起不小的轰动,有超越JAVA之势,本来在美国这个编程语言就是最火的,应用的非常非常的广泛,而Python的整体语言难度来讲又比JAVA简单的很多.尤其在运维的应用中非 ...

  3. usb口打印机的指令打印和驱动打印

    打印机简介:是计算机的输出设备之一,用于将计算机处理结果打印在相关介质上. 打印机类型:激光打印机.喷墨打印机.针式打印机.热敏打印机等. 计算机和打印机之间的连接方式:usb口.串口.并口.网口.蓝 ...

  4. c#基础二

    对VS2012的理解 1.导入命名空间 命名空间就是类的"文件夹".类就是"文件夹"中的文件.需要导入命名空间 添加引用: 如果我需要在一个项目中,访问另一个项 ...

  5. 孰能巧用 Spring Cloud 服务注册中心Eureka

    Eureka介绍 在Spring Cloud Netflix 整合技术栈中,Eureka既可以作为服务注册中心也可以用于服务发现对整个微服务架构起着最核心的整合作用. Eureka是基于REST(Re ...

  6. java 泛型?和T的区别

    泛型三种:          [1]ArrayList<T> al=new ArrayList<T>();指定集合元素只能是T类型          [2]ArrayList& ...

  7. 4.秋招复习简单整理之java支持多继承吗?

    java仅支持单继承,但支持接口多实现.

  8. Spring Cloud 之 Hystrix.

    一.概述  在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依 ...

  9. windows美化工具7+ Taskbar Tweaker

    今天分享一个windows美化工具 7+ Taskbar Tweaker 调整工具专为 Windows 任务栏工作者量身定制,支持 Windows 7 以及更高版本的(非服务器版)微软操作系统平台. ...

  10. k8s学习 - 概念 - Pod

    k8s学习 - 概念 - Pod 这篇继续看概念,主要是 Pod 这个概念,这个概念非常重要,是 k8s 集群的最小单位. 怎么才算是理解好 pod 了呢,基本上把 pod 的所有 describe ...