注解与AOP切面编程实现redis缓存与数据库查询的解耦
一般缓存与数据库的配合使用是这样的。
1.查询缓存中是否有数据。
2.缓存中无数据,查询数据库。
3.把数据库数据插入到缓存中。
其实我们发现 1,3 都是固定的套路,只有2 是真正的业务代码。我们可以把1,3 抽取出来,封装到一个自定义注解@myCache 上,通过给2方法加一个注解,实现代码的解耦。
package com.itbac.common.cache; import org.springframework.stereotype.Service; @Service
public class SkuQueryService {
//注解的使用
@myCache(key = "'SkuQueryService_findById' + #id")
public Object findById(String id){ System.out.println("findById方法查询数据库"); return "数据库返回值:"+id;
}
}
自定义注解
package com.itbac.common.cache; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; // 注解的生命周期:运行时
@Retention(RetentionPolicy.RUNTIME)
// 注解的应用范围:修饰方法
@Target(ElementType.METHOD)
public @interface myCache {
/**
* key 的生成规则,通过springEL表达式
* @return
*/
String key();
}
AOP切面类,切面编程,动态解析注解。
package com.itbac.common.cache; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component; import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit; /**
* Aop切面编程
*/
@Component
@Aspect
public class cacheAOP { @Autowired
private RedisTemplate redisTemplate;
// 环绕通知 : 监控自定义注解 。
@Around("@annotation(com.itbac.common.cache.myCache)")
public Object doAnyThing(ProceedingJoinPoint joinPoint) throws Throwable {
String key =null;
//1.反射技术:从注解里里面,读取key的生成规则。
//1.1 从切入点,获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//1.2 从切点,获取目标对象的字节码,的方法。参数:(方法名,方法的所有参数)。
Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
//1.3 从方法获取注解。
myCache annotation = method.getAnnotation(myCache.class);
//1.4 从注解,获取注解信息。'SkuQueryService_findById' + #id
String keyEL = annotation.key(); //2. 创建 springEL表达式 解析器
SpelExpressionParser parser = new SpelExpressionParser();
// 解析器 获取指定表达式 'SkuQueryService_findById' + #id 的表达式对象
Expression expression = parser.parseExpression(keyEL);
// 设置解析上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//2.1 创建默认参数名 发现者
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
//2.2 获取方法中的所有参数名。
String[] parameterNames = discoverer.getParameterNames(method);
//2.3 获取切点方法中的所有参数值。
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
// 把参数名,参数值,设置到解析器上下文
context.setVariable(parameterNames[i],args[i].toString());
}
//表达式 匹配 解析上下文 中的内容 ,拿到key
key = expression.getValue(context).toString(); Object o = redisTemplate.opsForValue().get(key);
if (o !=null){
// 缓存中有数据,直接返回。
//查询缓存
System.out.println("查询缓存返回。");
//延迟缓存失效时间,1天。
redisTemplate.expire(key,1,TimeUnit.DAYS);
return o;
}
// 缓存穿透标记
Object penetrateFlag = redisTemplate.opsForValue().get(key + "penetrateFlag");
if (null == penetrateFlag){
// 没有防止 缓存穿透标记,查数据库。 // 执行切点 中的代码,查询数据库。
Object proceed = joinPoint.proceed(); if (null == proceed){
// 数据库数据为空,设置缓存穿透标记。15分钟
redisTemplate.opsForValue().set(key + "penetrateFlag",true,15,TimeUnit.MINUTES);
}else {
// 数据库数据不为空,把数据存到缓存中。缓存1天。
redisTemplate.opsForValue().set(key,proceed,1, TimeUnit.DAYS);
}
return proceed ;
}
// 延迟缓存失效时间 15分钟 缓存穿透标记
redisTemplate.expire(key+"penetrateFlag",15,TimeUnit.MINUTES);
// 返回
return null;
}
}
这样就可以通过一个自定义注解@myCache ,实现了缓存与业务代码的解耦。其中还包含了防止缓存穿透的使用技巧。不要告诉别人哦。
注解与AOP切面编程实现redis缓存与数据库查询的解耦的更多相关文章
- SpringBoot 通过自定义注解实现AOP切面编程实例
一直心心念的想写一篇关于AOP切面实例的博文,拖更了许久之后,今天终于着手下笔将其完成. 基础概念 1.切面(Aspect) 首先要理解‘切’字,需要把对象想象成一个立方体,传统的面向对象变成思维,类 ...
- 注解配置AOP切面编程
1.导入先关jar包 2.编写applicationContext.xml,配置开启注解扫描和切面注解扫描 <?xml version="1.0" encoding=&quo ...
- 基于SpringBoot AOP面向切面编程实现Redis分布式锁
基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...
- AOP切面编程在android上的应用
代码地址如下:http://www.demodashi.com/demo/12563.html 前言 切面编程一直是一个热点的话题,这篇文章讲讲一个第三方aop库在android上的应用.第三方AOP ...
- SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务
本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.AOP切面编程 1.什么是AOP编程 在软件业,AOP为Asp ...
- 十:SpringBoot-配置AOP切面编程,解决日志记录业务
SpringBoot-配置AOP切面编程,解决日志记录业务 1.AOP切面编程 1.1 AOP编程特点 1.2 AOP中术语和图解 2.SpringBoot整合AOP 2.1 核心依赖 2.2 编写日 ...
- Spring MVC通过AOP切面编程 来拦截controller 实现日志的写入
首选需要参考的是:[参考]http://www.cnblogs.com/guokai870510826/p/5977948.html http://www.cnblogs.com/guokai8 ...
- Spring AOP 切面编程记录日志和接口执行时间
最近客户现在提出系统访问非常慢,需要优化提升访问速度,在排查了nginx.tomcat内存和服务器负载之后,判断是数据库查询速度慢,进一步排查发现是因为部分视图和表查询特别慢导致了整个系统的响应时间特 ...
- 本地缓存,Redis缓存,数据库DB查询(结合代码分析)
问题背景 为什么要使用缓存?本地缓存/Redis缓存/数据库查询优先级? 一.为什么要使用缓存 原因:CPU的速度远远高于磁盘IO的速度问题:很多信息存在数据库当中的,每次查询数据库就是一次IO操作所 ...
随机推荐
- 如何配置selinux
参考命令: 一.开启/关闭selinux getenforce:查看selinux运行状态 setenforce 0 :关闭selinux setenforce 1 :开启selinux 系统 ...
- [apue] popen/pclose 疑点解惑
问题请看这里: [apue] 使用 popen/pclose 的一点疑问 当时怀疑是pclose关闭了使用完成的管道,因此在pclose之前加一个足够长的sleep,再次观察进程文件列表: 哈哈,这下 ...
- 如何将 qsys 子模块设置为参数可调的方式给另外的qsys 调用
Intel FPGA Quartus 软件中的 Qsys工具 也就是 Platform Designer 系统集成工具,可以 图形化界面操作 使用系统自带ip,自定义ip 系统自动生成 ip 间的连接 ...
- Java并发包中线程池ThreadPoolExecutor原理探究
一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...
- 小代学Spring Boot之数据源
想要获取更多文章可以访问我的博客 - 代码无止境. 经过一天对Spring Boot的研究,小代同学已经对Spring Boot框架有了一个大概的认识.并且还创建了一个简单的Spring Boot的W ...
- 音频算法speex中的aec分析以及解析
算法原理: Speex的AEC是以NLMS(Normalized Least Mean Square)为基础,用MDF(multidelay block frequency domain)频域实现,最 ...
- Q&A-Ray-20180710
Q: 如果集群多个客户端订阅会不会重复接收消息? A: 集群环境用,有另外一个参数. NodeManager类没有在框架里面: public interface INodeManager : IGra ...
- 内核下载、nfs挂载:个性问题及解决方法~~共勉
开发板下载内核遇到starting waiting问题时: 首先想到是下载地址的问题! 将原本下载地址减去0X40,例如:smart210下载地址0X20008000,修改为0X20007FC0,即可 ...
- C++内存泄漏及检测工具详解
#include "stdafx.h" #ifdef _DEBUG #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, ...
- Git常用操作指南
目录 前言 Git简介 安装之后第一步 创建版本库 本地仓库 远程仓库 版本控制 工作区和暂存区 版本回退 撤销修改 删除文件 分支管理 创建与合并分支 解决冲突 分支管理策略 状态存储 多人协作 R ...