springboot自定义注解+aop+redis实现延时双删

这篇具有很好参考价值的文章主要介绍了springboot自定义注解+aop+redis实现延时双删。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

redis作为用的非常多的缓存数据库,在多线程场景下,可能会出现数据库与redis数据不一致的现象

数据不一致的现象:https://blog.csdn.net/m0_73700925/article/details/133447466

这里采用aop+redis来解决这个方法:

  1. 删除缓存
  2. 更新数据库
  3. 延时一定时间,比如500ms
  4. 删除缓存

这里之所以要延时一段时间再删除,是为了避免多线程情况下,更新数据库的操作还没执行,就执行了第二次删除缓存的操作,此时如果有请求进来,就会读取数据库并将数据写入缓存,这时再更新数据库就会导致数据不一致的问题

两次删除缓存是因为第一次删除缓存后,这时如果有请求进来,得到了数据并写入redis,然后再更新数据库,就会导致数据不一致

  1. 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface ClearAndReloadCache {
    String name() default "";
}
  1. 编写切面,以自定义注解作为切入点
@Aspect
@Component
public class ClearAndReloadCacheAspect {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(clearAndReloadCache)")
    public Object innerAround(ProceedingJoinPoint proceedingJoinPoint, ClearAndReloadCache clearAndReloadCache) throws Throwable {
        System.out.println("----------- 环绕通知 -----------");
        System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName());
        Signature signature1 = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature1;
        Method targetMethod = methodSignature.getMethod();//方法对象
        ClearAndReloadCache annotation = targetMethod.getAnnotation(ClearAndReloadCache.class);//反射得到自定义注解的方法对象
        String name = annotation.name();
        // 延时双删中的第一次删除缓存
        Set<String> keys = stringRedisTemplate.keys("*" + name + "*");
        stringRedisTemplate.delete(keys);

        Object proceed = null;
        // 执行业务层代码
        proceed = proceedingJoinPoint.proceed();

        // 执行延迟双删中的第二次删除缓存
        // 开启新线程是为了避免主线程堵塞等待
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                Set<String> keys2 = stringRedisTemplate.keys("*" + name + "*");
                stringRedisTemplate.delete(keys2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        return proceed;
    }
}

切面也可以写成这样,更方便理解

@Aspect
@Component
public class ClearAndReloadCacheAspect {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Pointcut("@annotation(com.toptolink.iot.permission.annotation.ClearAndReloadCache)")
    public void pointCut(){

    }

    @Around("pointCut()")
    public Object innerAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("----------- 环绕通知 -----------");
        System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName());
        Signature signature1 = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature1;
        Method targetMethod = methodSignature.getMethod();//方法对象
        ClearAndReloadCache annotation = targetMethod.getAnnotation(ClearAndReloadCache.class);//反射得到自定义注解的方法对象
        String name = annotation.name();
        // 延时双删中的第一次删除缓存
        Set<String> keys = stringRedisTemplate.keys("*" + name + "*");
        stringRedisTemplate.delete(keys);

        Object proceed = null;
        // 执行业务层代码
        proceed = proceedingJoinPoint.proceed();

        // 执行延迟双删中的第二次删除缓存
        // 开启新线程是为了避免主线程堵塞等待
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                Set<String> keys2 = stringRedisTemplate.keys("*" + name + "*");
                stringRedisTemplate.delete(keys2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        return proceed;
    }
}
  1. 业务层代码
    controller
@ApiOperation("小程序小程序平台通用-工单详情")
    @GetMapping("/queryWorkOrderDetail")
    @PreAuthorize(Permissions.YQZ_MAINTAIN)
    public DataResponseBody queryWorkOrderDetail(@RequestParam Long id) {
        return new DataResponseBody(iMaintainService.queryWorkOrderDetail(id));
    }

    /**
     * @return
     */
    @GetMapping("/TODOupdateById")
    @PreAuthorize(Permissions.YQZ_MAINTAIN)
    @ClearAndReloadCache(name = "getById")
    public DataResponseBody TODOupdateById(Long id, String customerFullName) {
        return new DataResponseBody(iMaintainService.TODOupdateById(id, customerFullName));
    }

我在queryWorkOrderDetail()中将查到的数据存入了redis中,redisService.set("getById"+id, json);文章来源地址https://www.toymoban.com/news/detail-795907.html

  1. 测试
    首先需要通过idea多开启一个程序,用于模拟多线程
    springboot自定义注解+aop+redis实现延时双删,spring boot,redis,java
    springboot自定义注解+aop+redis实现延时双删,spring boot,redis,java
    然后通过打断点的方式
    springboot自定义注解+aop+redis实现延时双删,spring boot,redis,java
  • 首先调用查询接口,此时会将数据存入redis中
  • 然后调用修改接口,进入debug模式,当第一次删除缓存后,不要往下走
  • 再次调用查询接口,用于模拟多线程情况下的数据不一致情况
  • 这时redis又会存入数据
  • 接着就是更新数据库的操作
  • 此时如果没有第二次删除缓存,就会出现数据不一致了
  • 所以第二次删除缓存是很有必要的

到了这里,关于springboot自定义注解+aop+redis实现延时双删的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring】使用自定义注解方式实现AOP鉴权

    AOP,是一种面向切面编程,可以通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 在软件开发中,鉴权(Authentication)是一项非常重要的安全措施,用于验证用户身份和权限。在应用程序中,我们通常会使用AOP(Aspect-Oriented Programming)来实现鉴权功能

    2024年02月11日
    浏览(47)
  • Spring Boot学习随笔- 实现AOP(JoinPoint、ProceedingJoinPoint、自定义注解类实现切面)

    学习视频:【编程不良人】2021年SpringBoot最新最全教程 问题 现有业务层开发存在问题 额外功能代码存在大量冗余 每个方法都需要书写一遍额外功能代码不利于项目维护 Spring中的AOP AOP:Aspect 切面 + Oriented 面向 Programmaing 面向切面编程 Aspect(切面) = Advice(通知) + Pointcut(

    2024年02月04日
    浏览(48)
  • 【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战

                                                 💧 S p r i n g A O P + 主从数据源切换 + 读写分离 + 自定义注解案例实战! color{#FF1493}{Spring AOP + 主从数据源切换 + 读写分离 + 自定义注解 案例实战!} Sp r in g A OP + 主从数据源切换 + 读写分离 + 自定义注解案例

    2024年02月15日
    浏览(39)
  • 图文讲解Redis延时双删原因及必要性

    目录 一、前言 二、常见更新策略 2.1 先删缓存,再更新数据库 2.2 先更新数据库,再删除缓存 2.3 普通双删 2.4 延迟双删 三、是否必要建议 我们在实际项目中经常会使用到Redis缓存用来缓解数据库压力,但是当更新数据库时,如何保证缓存及数据库一致性,一般我们采用延时

    2024年02月16日
    浏览(38)
  • Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截

    🏷️ 个人主页 :牵着猫散步的鼠鼠  🏷️ 系列专栏 :Java全栈-专栏 🏷️ 个人学习笔记,若有缺误,欢迎评论区指正   前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站AI学习网站。 目录 前言 1.导入Redisson 引入依

    2024年02月21日
    浏览(54)
  • Spring Boot入门(23):基于AOP实现自定义注解拦截接口日志并保存入库 | 超级详细,建议收藏

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

    2024年02月11日
    浏览(48)
  • spring自定义注解+aop+@BindingParam

    2.1 声明切面注解  2.1.1切面对应枚举  2.2 声明绑定参数注解 4.1 ThreadLocalUtil  4.2  自定义异常

    2024年02月14日
    浏览(40)
  • springboot aop 自定义注解形式

    2024年01月25日
    浏览(38)
  • spring boot 使用AOP+自定义注解+反射实现操作日志记录修改前数据和修改后对比数据,并保存至日志表

    使用FieldMeta自定义注解,看个人业务自行觉得是否需要重新定义实体 实现类 :通过该实现类获取更新前后的数据。 该实现类的实现原理为:获取入参出入的id值,获取sqlSessionFactory,通过sqlSessionFactory获取selectByPrimaryKey()该方法,执行该方法可获取id对应数据更新操作前后的数

    2024年01月23日
    浏览(52)
  • 【SpringBoot】AOP 自定义注解的使用详解

            Spring 中的切面 Aspect,这是 Spring 的一大优势。面向切面编程往往让我们的开发更加低耦合,也大大减少了代码量,同时呢让我们更专注于业务模块的开发,把那些与业务无关的东西提取出去,便于后期的维护和迭代。         AOP 的全称为 Aspect Oriented Programming,

    2024年02月05日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包