SpringBoot利用AOP线程池异步设置缓存

news2024/9/21 14:47:44

文章目录

  • 设置缓存
    • 1、定义注解
    • 2、AOP
    • 3、测试

设置缓存

1、定义注解

注解定义四个属性,分别是:

  • value,key的别名
  • key : redis的key,如果key不设置,则会用方法名加参数列表作为key
  • expire:失效时间,默认为 1天
  • TimeUnit : 时间单位,默认为秒
import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
    @AliasFor("key")
    String value() default "";

    String key() default "";

    long expire() default 24 * 60 * 60L;

    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

2、AOP

这个Aop的作用:

  • 查redis缓存
  • 设置缓存-通过线程池异步设置缓存
    里面用到了fastJson、lombok
    依赖
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
import com.alibaba.fastjson.JSON;
import com.sifan.erp.annotation.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Slf4j
public class RedisCacheAOP {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 线程池,目标方法执行完之后直接返回结果,设置缓存的工作异步执行,这样能提高运行效率*
     */
    private static ThreadPoolExecutor threadPoolExecutor;

    static {
        // 初始化线程池
        threadPoolExecutor = new ThreadPoolExecutor(1, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
    }

    /**
     * 切入点 @RedisCache注解作为切入点*
     */
    @Pointcut("@annotation(com.sifan.erp.annotation.RedisCache)")
    public void RedisCacheAOP() {
    }

    @Around("RedisCacheAOP()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法上的@RedisCache注解
        RedisCache redisCacheAnnotation = methodSignature.getMethod().getAnnotation(RedisCache.class);
        // 判断key或value是否为空,有一个不为空则作为redis的key,如果都存在则取key的值
        String redisKey = null;
        String key = redisCacheAnnotation.key();
        String value = redisCacheAnnotation.value();
        if (value != null && !"".equals(value)) {
            redisKey = value;
        }
        if (key != null && !"".equals(key)) {
            redisKey = key;
        }
        // AOP返回的结果
        Object result = null;
        // 到这如果redisKey为null,没进上面的两个判断,表示key与value为空,使用方法名与参数列表作为redisKey
        if (redisKey == null) {
            String methodName = methodSignature.getName();
            int index = methodName.indexOf('$');
            if (index == -1) {
                index = methodName.lastIndexOf('.');
            }
            methodName = methodName.substring(index + 1);
            // 获取参数列表
            Object[] args = joinPoint.getArgs();
            if (args != null) {
                StringBuilder sb = new StringBuilder();
                sb.append(methodName);
                sb.append(":");
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    // 把参数转为string类型,有些参数是对象
                    String stringArg = JSON.toJSONString(arg);
                    sb.append(stringArg);
                    // 每个参数分割开
                    if (i != args.length - 1) {
                        sb.append(":");
                    }
                }
                redisKey = sb.toString();
            }
        }
        // ok 现在key搞定了
        // 判断key在redis是否存在值,存在直接返回结果,不执行目标方法
        Object redisRes = redisTemplate.opsForValue().get(redisKey);
        log.info("缓存key = {}", redisKey);
        if (redisRes != null) {
            //  如果redis有值,直接返回
            log.info("获取redis缓存成功 key = {}", redisKey);
            result = redisRes;
        } else {
            // 没有值,执行目标方法,设置值,返回
            Object proceed = joinPoint.proceed();
            result = proceed;
            Long expire = redisCacheAnnotation.expire();
            TimeUnit timeUnit = redisCacheAnnotation.timeUnit();
            // copyRedisKey 必须final修饰,因为异步执行需要copyRedisKey,线程池会复制一份到execute中,这时会出现两份copyRedisKey,线程池为了保存两份copyRedisKey一致,必须用fina修饰
            final String copyRedisKey = redisKey;
            // 异步设置缓存
            threadPoolExecutor.execute(() -> {
                redisTemplate.opsForValue().set(copyRedisKey, proceed, expire, timeUnit);
                log.info("异步设置缓存成功 key = {}", copyRedisKey);
            });
        }
        return result;
    }

}



3、测试

在Service中定义一个redisCacheService方法,加上注解@RedisCache
1、测试简单参数,简单结果

    @RedisCache
    public String redisCacheService(Integer id, String name) {
        return "我是缓存";
    }

测试类

    @Resource
    private UserServiceImpl userService;

    @Test
    public void testRedisCache_1() {
        String res = userService.redisCacheService(100, "二狗");
        System.out.println(res);
    }

结果

第一次执行方法,异步设置缓存
在这里插入图片描述
在这里插入图片描述第二次执行方法,直接获取缓存

在这里插入图片描述2、测试复杂参数,复杂结果
当然一般需要设置的缓存的方法的参数是简单参数直接使用 @RedisCache(expire = 60)即可,expire 设置失效时间,单位默认为秒
在这里插入图片描述

在这里插入图片描述3、自己设置key

@RedisCache(key = "userCache")

在这里插入图片描述在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dockerfile创建镜像并上传到Docker Hub

Dockerfile创建镜像并上传到Docker Hub1. Dockerfile1.1 准备文件1.2 构建镜像2. 上传docker hub参考1. Dockerfile 通过Dockerfile构建镜像 1.1 准备文件 在某个空文件夹&#xff08;假设名为test&#xff09;下编写Dockerfile文件 # 声明使用哪个基础镜像 FROM ubuntu:20.0…

Spring Cloud微服务网关Gateway组件学习笔记

目录 网关简介 1. 什么是Spring Cloud Gateway 1.1 核心概念 1.2 工作原理 2. Spring Cloud Gateway快速开始 2.1 环境搭建 2.2集成Nacos 3. 路由断言工厂&#xff08;Route Predicate Factories&#xff09;配置 3.1内置断言工厂 3.2自定义 4. 过滤器工厂&#xf…

Android Studio实现志愿者系统

项目目录一、项目概述二、主要技术三、开发环境四、详细设计1、基础Activity2、活动信息3、成员信息4、百度地图5、Widget组件五、运行演示一、项目概述 本系统采用MVC架构设计&#xff0c;SQLite数据表有用户表、成员表和活动表&#xff0c;有十多个Activity页面。打开应用&a…

123、【回溯算法】leetcode ——491. 递增子序列:unordered_set去重和int数组去重(C++版本)

题目描述 原题描述&#xff1a;491. 递增子序列 解题思路 此题也是子集问题&#xff0c;但和 90.子集II &#xff08;子集问题startIndex去重&#xff09; 的区别在于&#xff1a;&#xff08;1&#xff09;存储结果集判定条件&#xff1b;&#xff08;2&#xff09;输入数据…

Python学习笔记十二之十大经典排序算法

Python学习笔记十二之十大经典排序算法 排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;…

zos JESMSGLG 不显示 job steps return codes

zos JESMSGLG 不显示 job steps return codes 在普通学习的时候&#xff0c;SDSF 查看 JCL 结果&#xff0c;可能并不会注意到下面结果有什么差别 但跟公司的大型机出的结果是不同的&#xff0c;就是缺少了 job steps return codes&#xff0c;在 STARTED 和 ENDED 中间应该有…

【Linux】冯诺依曼体系结构和操作系统

目录 一、冯诺依曼体系结构 1.组成 2.各结构特性 二、操作系统 1.概念 2.设计OS的目的 3.如何理解 "管理" 4.系统调用 一、冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依…

浅析故障电弧探测器在电气防火中的作用与应用介绍

安科瑞 李雨轩【摘要】&#xff1a; 分析了重大电气火灾数据。结合目前国内前沿的电气火灾探测技术&#xff0c;重点介绍了故障电弧式电气火灾监控探测器在火灾预警系统中的重要作用&#xff0c;给出了基于多种探测技术的电气火灾监控系统的实现方案。【关键词】&#xff1a;故…

C++模板初阶小笔记

目录 一.泛型编程 二.函数模板 1.函数模板语法梳理&#xff1a; 2.函数模板的实例化&#xff1a; 3.函数模板的显式实例化&#xff1a; 4.函数模板使用时的注意事项 三.类模板 1.类模板的语法梳理 2.类模板中声明和定义分离的成员函数 一.泛型编程 泛型编程&#xff…

【C++】类和对象---什么是类?

目录1.面向过程和面向对象初步认识2.类的引入2.1使用struct定义类3.类的定义3.1类的两种定义方式&#xff1a;3.2成员变量命名规则的建议3.3成员函数与成员变量定义的位置建议4.类的访问限定符及封装4.1访问限定符4.2封装5.类的作用域6.类的实例化7.类对象模型7.1如何计算类对象…

Kali Linux渗透

Kali Linux是基于Debian的Linux发行版&#xff0c; 设计用于数字取证操作系统。每一季度更新一次。 黑盒测试&#xff0c;它是通过测试来检测每个功能是否都能正常使用。在测试中&#xff0c;把程序看作一个不能打开的黑盒子&#xff0c;在完全不考虑程序内部结构和内部特性的情…

从C语言的使用转换到C++(上篇)——刷题、竞赛篇

文章目录 一、C的基础语法详解 1、1 输入、输出流 1、2 C中头文件的使用 1、3 C中变量的声明 1、4 C中的string类 1、5 C中的引用& 二、C中常见函数使用详解 2、1 排序sort函数详解 2、2 cctype头文件中的函数 三、总结 标题&#xff1a;从C语言的使用转换到C&#xff08;上…

④【Spring】IOC - 基于注解方式 管理bean

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 注解管理Bean一、注解的功能二、四个典型注解三…

day24|491.递增子序列、46.全排列、47.全排列 II

491.递增子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情况…

源码深度解析Spring Bean的创建,手把手的一步一步带你看源码

在源码深度解析Spring Bean的加载 中对Bean的加载源码进行了梳理,在本文将对bean的创建进行梳理 在doGetBean()方法中createBean()方法准备创建bean,调用源码如下: createBean() 方法的源码如下: 从代码中可以得出,createBean() 方法涉及4个步骤: 步骤一: 根据设置的class属…

[论文笔记]A ConvNet for the 2020s

目录 Abstract Modernizing a ConvNet: a Roadmap 2.1.Training Techniques 2.2. Macro Design 2.3. ResNeXt-ify 2.4. Inverted Bottleneck 2.5. Large Kernel Sizes 2.6. Micro Design 论文&#xff1a;https://arxiv.org/abs/2201.03545 代码&#xff1a;GitHub - f…

扩散模型(Diffusion model)代码详细解读

扩散模型代码详细解读 代码地址&#xff1a;denoising-diffusion-pytorch/denoising_diffusion_pytorch.py at main lucidrains/denoising-diffusion-pytorch (github.com) 前向过程和后向过程的代码都在GaussianDiffusion​这个类中。​ 常见问题解决 Why self-condition…

盒相关样式-----diaplay:block、inline

盒的基本类型 CSS中的盒分为block类型与inline类型&#xff0c;例如&#xff0c; div元素与p元素属于block类型&#xff0c; span元素与a元素属于inline类型。 block类型的盒对应的是html中的块级元素&#xff0c;inline类型的盒对应了html中的行内元素。 行内元素与块级元素…

JavaScript 练手小技巧:键盘事件

键盘事件应该是鼠标事件之外&#xff0c;使用频率最高的 JS 事件了吧&#xff1f; 一般用于全局或者表单。 键盘事件由用户击打键盘触发&#xff0c;主要有keydown、keypress、keyup三个事件。 keydown&#xff1a;按下键盘时触发。Ctrl、Shift、Alt 等和其它按键组合时&…

BCNF与3NF

今天学了一下午这个BCNFBCNFBCNF与3NF3NF3NF&#xff0c;有感而发&#xff0c;特来总结。好像好久不打键盘了&#xff0c;这手好像刚长出来的一样。本文浅显的分析一下两种范式的关系与不同以及判断方法和分解算法&#xff0c;以做总结。 BCNFBCNFBCNF范式的定义如下: 设属性集…