• SpringBoot+Redis实现接口级别缓存信息


    本文主要讲述如何通过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 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自带的,而需要自己去写,主要原因还是这是一个老系统,压根找不全对数据进行修改、删除的地方


    如有哪里讲得不是很明白或是有错误,欢迎指正
    如您喜欢的话不妨点个赞收藏一下吧🙂

  • 相关阅读:
    Redis的RDB持久化
    Linux的系统进程详解
    Visual Studio 2022 安装
    源码分析:实现 Actor 系统
    韦东山linux驱动开发学习【常更】
    视频剪辑技巧:如何高效地将多个视频合并成一个新视频
    双十一什么数码好物值得买?双十一最值得买的数码好物分享
    RestCloud ETL WebService数据同步到本地
    uniapp 获取地理位置(uni#getLocation和高德sdk获取中文地址)
    react 中组件的传参 怎么设置为可选的,比如加上?
  • 原文地址:https://www.cnblogs.com/ancold/p/17807294.html