本文主要讲述如何通过SpringBoot+Redis实现接口级别缓存信息

背景

近期因为一直在处理公司的老项目,恰好碰到产品说页面有一些信息展示慢,简单看了一下页面接口,发现查询的是系统中几张大表(数据量在千万级别),还会关联一些其他的表,导致接口性能极差,但是由于这些信息也不存在"及时性"这么一说,便想着通过接口缓存来控制

相关技术

jdk 1.8
reids 5.0.7

实现思路

通过注解来标识需要缓存的接口,依据注解的内容去找到对应的建造者,通过建造者来找到具体去执行的类,最终达可扩展+缓存的效果

注解相关代码

package com.com.example.springdemo;

import com.com.example.springdemo.aspect.enums.RedisCacheEnums;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit; /**
* @CreateAt: 2023-11-3 11:25:37
* @ModifyAt: 2023-11-3 11:25:37
* @Version 1.0
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCache { /**
* 缓存的key
**/
String key() default ""; /**
* 参数类型
*
* @return
*/
RedisCacheEnums type(); /**
* 缓存时长,默认-1表示永久有效
**/
int time() default 300; /**
* 缓存时长单位,默认单位秒
**/
TimeUnit timeType() default TimeUnit.SECONDS;
}

枚举相关代码


package com.example.springdemo.enums; import java.util.Arrays; /**
* 缓存类型
*
* @CreateAt: 2023-11-3 11:26:22
* @ModifyAt: 2023-11-3 11:26:22
* @Version 1.0
*/
public enum RedisCacheEnums { QUERY_MEMBER_INFO(1, "查询会员信息"),
; private Integer code;
private String type; RedisCacheEnums(Integer code, String type) {
this.code = code;
this.type = type;
} public static RedisCacheEnums getByCode(int code) {
return Arrays.stream(RedisCacheEnums.values())
.filter(item -> item.getCode().intValue() == code)
.findFirst()
.orElse(null);
} public Integer getCode() {
return code;
} public String getType() {
return type;
}
}

切换相关代码

package com.example.springdemo.aspect;

import com.example.springdemo.RedisCacheProvider;
import com.example.springdemo.handler.RedisCacheHandler;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
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.stereotype.Component; /**
* @CreateAt: 2023-11-3 11:27:20
* @ModifyAt: 2023-11-3 11:27:20
* @Version 1.0
*/
@Aspect
@Slf4j
@Component
@AllArgsConstructor
public class RedisCacheAspect { @Pointcut("@annotation(com.example.springdemo.RedisCache)")
public void annotationPoint() throws Throwable { } private RedisCacheProvider redisCacheProvider; /**
* 却面切入点
* implements Serializable 对象需要继承
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around(value = "annotationPoint()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
MethodSignature method = (MethodSignature) proceedingJoinPoint.getSignature();
RedisCache redisCache = method.getMethod().getAnnotation(RedisCache.class);
Object[] args = proceedingJoinPoint.getArgs();
RedisCacheHandler apply = redisCacheProvider.apply(redisCache.type());
Boolean hasKey = apply.existHandler(args, redisCache.key());
if (hasKey) {
return apply.queryHandler(args, redisCache.key());
} else {
Object result = proceedingJoinPoint.proceed();
apply.handler(redisCache.type(), args, result, redisCache.time(), redisCache.timeType(), redisCache.key());
return result;
}
} catch (Exception e) {
log.info("RedisCacheAspect Error:{}", e.toString());
return proceedingJoinPoint.proceed();
}
}
}

建造者和相关hannder对应代码

package com.example.springdemo;

import com.example.springdemo.enums.RedisCacheEnums;
import com.example.springdemo.handler.RedisCacheHandler;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert; import java.util.List; /**
* 缓存提供者
* @CreateAt: 2023-11-3 11:28:42
* @ModifyAt: 2023-11-3 11:28:42
* @Version 1.0
*/
@Component
@AllArgsConstructor
@Slf4j
public class RedisCacheProvider { private List<RedisCacheHandler> handlers; public RedisCacheHandler apply(RedisCacheEnums type) {
RedisCacheHandler redisCacheHandler = handlers.stream().filter(x -> x.support(type)).findFirst().orElse(null);
Assert.notNull(redisCacheHandler, "未找到对应的处理器");
return redisCacheHandler;
}
}
package com.example.springdemo.handler;

import com.example.springdemo.enums.RedisCacheEnums;

import java.util.concurrent.TimeUnit;

/**
* @CreateAt: 2023-11-3 11:29:39
* @ModifyAt: 2023-11-3 11:29:39
* @Version 1.0
*/
public interface RedisCacheHandler { /**
* 是否支持处理
*
* @param type
* @return
*/
boolean support(RedisCacheEnums type); /**
* 查询缓存信息
*
* @param args
* @param originalKey
* @return
*/
Object queryHandler(Object[] args, String originalKey) throws Exception; /**
* 缓存信息是否存在
*
* @param args
* @param originalKey
* @return
*/
Boolean existHandler(Object[] args, String originalKey) throws Exception; /**
* 生成缓存信息
*
* @param type 类型
* @param args 参数
* @param result 结果
* @param time 时间
* @param timeType 时间类型
* @param originalKey
*/
void handler(RedisCacheEnums type, Object[] args, Object result, Integer time, TimeUnit timeType, String originalKey) throws Exception;
}
package com.example.springdemo.handler;

import com.example.springdemo.enums.RedisCacheEnums;
import com.example.springdemo.common.utils.RedisUtil;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /**
* @CreateAt: 2023-11-3 11:30:30
* @ModifyAt: 2023-11-3 11:30:30
* @Version 1.0
*/
@Slf4j
@Service
@AllArgsConstructor
public class MemberHandler implements RedisCacheHandler { private final String redisKey = "test"; // 切换成自己项目使用的redis工具类即可
private RedisUtil resdisUtil; /**
* 是否支持处理
*
* @param type
* @return
*/
@Override
public boolean support(RedisCacheEnums type) {
return RedisCacheEnums.QUERY_MEMBER_INFO.equals(type);
} /**
* 查询缓存信息
*
* @param args
* @param originalKey
* @return
*/
@Override
public Object queryHandler(Object[] args, String originalKey) throws Exception {
String key = getKey(args, originalKey);
return resdisUtil.get(key);
} /**
* 查询缓存信息
*
* @param args
* @param originalKey
* @return
*/
@Override
public Boolean existHandler(Object[] args, String originalKey) throws Exception {
String key = getKey(args, originalKey);
return resdisUtil.hasKey(key);
} /**
* 生成操作记录对象
*
* @param type
* @param args
* @param result
* @param time
* @param timeType
* @param originalKey
* @return
*/
@Override
public void handler(RedisCacheEnums type, Object[] args, Object result, Integer time, TimeUnit timeType, String originalKey) throws Exception {
String key = getKey(args, originalKey);
resdisUtil.setByTime(key, result, time, timeType);
} /**
* 获取Key信息
*
* @param args
* @return
*/
private String getKey(Object[] args, String originalKey) throws Exception {
try {
Object omiMemberInquiryVO = (Object ) args[0];
// 拼装缓存key信息
String key = "test";
log.info("RedisCacheAspect key:{}",key);
return key;
} catch (Exception e) {
log.info("RedisCacheAspect 拼装Key参数异常:{},e:{}", new Gson().toJson(args), e.toString());
throw new Exception("拼装Key参数异常");
}
}
}

手动ps:可能很多人都会问为什么不用Spring自带的,而需要自己去写,主要原因还是这是一个老系统,压根找不全对数据进行修改、删除的地方


如有哪里讲得不是很明白或是有错误,欢迎指正

如您喜欢的话不妨点个赞收藏一下吧

SpringBoot+Redis实现接口级别缓存信息的更多相关文章

  1. 【Redis】SpringBoot+Redis+Ehcache实现二级缓存

    一.概述 1.1 一些疑惑? 1.2 场景 1.3 一级缓存.两级缓存的产生 1.4 流程分析 二.项目搭建 一.概述 1.1 一些疑惑? Ehcache本地内存 Redis 分布式缓存可以共享 一级 ...

  2. SpringBoot + redis + @Cacheable注解实现缓存清除缓存

    一.Application启动类添加注解 @EnableCaching 二.注入配置 @Bean public CacheManager cacheManager(RedisTemplate redi ...

  3. springboot redis 缓存对象

    只要加入spring-boot-starter-data-redis , springboot 会自动识别并使用redis作为缓存容器,使用方式如下 gradle加入依赖 compile(" ...

  4. spring boot 学习(十四)SpringBoot+Redis+SpringSession缓存之实战

    SpringBoot + Redis +SpringSession 缓存之实战 前言 前几天,从师兄那儿了解到EhCache是进程内的缓存框架,虽然它已经提供了集群环境下的缓存同步策略,这种同步仍然需 ...

  5. springboot+redis实现缓存数据

    在当前互联网环境下,缓存随处可见,利用缓存可以很好的提升系统性能,特别是对于查询操作,可以有效的减少数据库压力,Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存 ...

  6. springboot + redis + 注解 + 拦截器 实现接口幂等性校验

    一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多 ...

  7. Springboot + redis + 注解 + 拦截器来实现接口幂等性校验

    Springboot + redis + 注解 + 拦截器来实现接口幂等性校验   1. SpringBoot 整合篇 2. 手写一套迷你版HTTP服务器 3. 记住:永远不要在MySQL中使用UTF ...

  8. SpringBoot + Redis:基本配置及使用

    注:本篇博客SpringBoot版本为2.1.5.RELEASE,SpringBoot1.0版本有些配置不适用 一.SpringBoot 配置Redis 1.1 pom 引入spring-boot-s ...

  9. Redis+Caffeine两级缓存,让访问速度纵享丝滑

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 在高性能的服务架构设计中,缓存是一个不可或缺的环节.在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中, ...

  10. Redis基础知识之—— 缓存应用场景

    转载原文:http://www.cnblogs.com/jinshengzhi/articles/5225718.html 一.MySql+Memcached架构的问题 Memcached采用客户端- ...

随机推荐

  1. linux_mint_21 vim配置:剪贴板支持和输入法自动切换

    一.vim的剪贴板支持 有的vim版本不支持系统剪切板,也就是说在vim编辑器外面复制的内容,不能够粘贴到vim中;在vim中通过yy.d.c复制剪切的内容也不能粘贴在vim编辑器外面. 这实在是有点 ...

  2. Python数据分析易错知识点归纳(一):基础知识

    一.python基础 字符串replace方法 txt = txt.replace(s, ' ') # 光是txt.replace(s, ' ')是不会对txt产生影响的 # 下面每次循环replac ...

  3. .NET Core中关于阿拉伯语环境下的坑:Input string was not in a correct format.

    结论 .NET Core项目(.NET Framework没出现)在阿拉伯语(即语言名称是ar-开头的语言)环境下,将负数字符串转成数字,即int.Parse("-1")或Conv ...

  4. linux基础命令及常用命令总结

    1.ls命令 ls命令是最基础的命令之一,作用是列出当前目录下所有的文件和目录.ls命令有很多选项可以使用,比较常用的是-l选项,可以以详细信息的形式列出所有文件和目录的信息. 示例:列出当前目录下的 ...

  5. 小白也能看懂的 ROC 曲线详解

    作者:PrimiHub-Kevin ROC 曲线是一种坐标图式的分析工具,是由二战中的电子和雷达工程师发明的,发明之初是用来侦测敌军飞机.船舰,后来被应用于医学.生物学.犯罪心理学. 如今,ROC 曲 ...

  6. Windows查询进程和杀死进程

    查询进程 查询进程占用的端口(通过端口查询) netstat -ano |findstr "端口号" 杀死进程 通过进程号查看所属进程名称 tasklist |findstr &q ...

  7. MyBatis使用注解开发(及Sqlsession连接器的本质)

    使用注解开发 底层实现机制是反射和,动态代码.反射可以获得这个类的方法属性还可以创建对象,执行方法. 面向接口编程 之前学过,面向对象编程,也学习过接口.但是真正的开发中,很多时候我们会选择面向接口编 ...

  8. LeetCode 周赛上分之旅 #40 结合特征压缩的数位 DP 问题

    ️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问. 学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越 ...

  9. [ABC132D] Blue and Red Balls

    2023-01-16 题目 题目传送门 翻译 翻译 难度&重要性(1~10):3 题目来源 AtCoder 题目算法 dp 解题思路 因为蓝球的数量是固定的,题目让我们求,在取 \(i\) 次 ...

  10. from my mac

    hello