百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

利用AOP自定义Redis缓存注解

toyiye 2024-06-21 19:09 12 浏览 0 评论


背景

在查询类开发中我们有使用缓存的场景,一般可以使用Redis作为缓存,来缓解数据库如MySQL的压力。使用缓存的步骤为:

(1)从Redis缓存中获取数据,如果存在数据,直接返回值。

(2)如果不存在,执行数据库的查询方法

(3)将数据库中的值放入缓存

NO CODE NO BB,代码如下

//a.从缓存中获取
String value = redisTemplate.opsForValue().get(key);
if (value != null) {
    log.info("从缓存中读取到值:{}", value);
    return value;
}

//b.从数据库中查询
List<Member> members = memberMapper.listByName(name);

//c.同步value到缓存
value = JSONArray.toJSONString(members);
redisTemplate.opsForValue().set(key, value, expireTimes, TimeUnit.SECONDS);
return value;

如上代码,这里有个问题,我们只是要做个查询而已,也就是只要b行的代码,其他代码不是业务代码,不应该由开发人员去操心。那我们何不用注解的形式代替a和c代码呢。

使用SpringBoot的缓存注解

SpringBoot提供了现成可用的缓存注解@Cacheble。

配置类开启缓存注解

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        ...
     }
     ...
         
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60))      //缓存过期时间
                .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }
 }

使用@Cacheable注解

@Cacheable(value = "member",key = "#name")
public List<Member> listByName(String name) {
    return memberMapper.listByName(name);
}

测试

@Test
void listByName() {
    String name = "zhouzhou";
    List<Member> members = memberController.listByName(name);
    log.info("members:{}",members);
}

控制台结果

members:[Member(id=1805590839001216, name=zhouzhou, code=109, annotationParam=null)]

上面代码发现使用Spring缓存注解的缓存失效时间还要在配置类中进行配置。于是我在想为什么这个失效时间不做成注解的这一项属性呢,这样自定义失效时间就比较方便了。

自定义缓存注解

注解定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomizeCache {

    String key();

    String value();

    long expireTimes() default 120L; //默认过期时间120s

    int semaphoreCount() default Integer.MAX_VALUE;  //默认限制线程并发数
}

参数说明如下:

key():缓存的key,一般是一个动态的参数值

vaule():缓存的value,value::key为Redis缓存中拼接的KEY

expireTimes():缓存失效时间,默认

semaphoreCount():共享锁,并发下允许访问的线程数,用于保护数据库。默认为Integer.MAX_VALUE

AOP注解开发

首先创建切面类

@Component
@Aspect
@Slf4j
public class CacheAspect {
   ...
}

创建横切面,为注解CustomizeCache添加功能。

@Pointcut("@annotation(com.lvshen.demo.redis.cache.CustomizeCache)")
    public void cachePointcut() {
    }

开发缓存功能,定义@Around

 @Around("cachePointcut()")
    public Object doCache(ProceedingJoinPoint point) {
    ...
    }

获取方法上注解的内容

Method method = point.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
CustomizeCache annotation = method.getAnnotation(CustomizeCache.class);
String keyEl = annotation.key();
String prefix = annotation.value();
long expireTimes = annotation.expireTimes();
int semaphoreCount = annotation.semaphoreCount();

解析SpringEL表达式

Object[] args = point.getArgs();
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
    context.setVariable(parameterNames[i], args[i].toString());
}

拼接Redis KEY

//解析
String key = prefix + "::" + expression.getValue(context).toString();

判断缓存中是否存在

value = redisTemplate.opsForValue().get(key);
if (value != null) {
    log.info("从缓存中读取到值:{}", value);
    return value;
}

自定义组件-创建限流令牌

semaphore = new Semaphore(semaphoreCount);
boolean tryAcquire = semaphore.tryAcquire(3000L, TimeUnit.MILLISECONDS);
if (!tryAcquire) {
    //log.info("当前线程【{}】获取令牌失败,等待其他线程释放令牌",   Thread.currentThread().getName());
    throw new RuntimeException(String.format("当前线程【%s】获取令牌失败,等带其他线程释放令牌", Thread.currentThread().getName()));
}

如果缓存没有数据,则执行原本方法。

 value = point.proceed();

同步value到缓存

redisTemplate.opsForValue().set(key, value, expireTimes, TimeUnit.SECONDS);

最后释放令牌

} finally {
    if (semaphore == null) {
     return value;
    } else {
     semaphore.release();
    }
}

调用注解

@CustomizeCache(value = "member", key = "#name")
public List<Member> listByNameSelfCache(String name) {
 return memberMapper.listByName(name);
}

测试

@Test
void testCache() {
    String name = "lvshen99";
    List<Member> members = memberService.listByNameSelfCache(name);
    log.info("members:{}",members);
}

测试结果

members:[Member(id=15, name=lvshen99, code=200, annotationParam=null)]

Redis上显示

我们也可以自定义缓存失效时间,如设置失效时间

 @CustomizeCache(value = "member", key = "#name",expireTimes = 3600)
 public List<Member> listByNameSelfCache(String name) {
  return memberMapper.listByName(name);
 }

失效时间为,如图,显示是因为截图的时候过了。

源码地址如下:

完整源码Github地址:https://github.com/lvshen9/demo/tree/lvshen-dev/src/main/java/com/lvshen/demo/redis/cache

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码