基于装饰模式实现多级缓存

1. 回顾多级缓存基本概念

在实际开发项目,为了减少数据库的访问压力,我们都会将数据缓存到内存中比如:Redis(分布式缓存)、EHCHE(JVM内置缓存)。
例如在早起中,项目比较小可能不会使用Redis做为缓存,使用JVM内置的缓存框架,项目比较大的时候开始采用Redis分布式缓存框架,这时候需要设计一级与二级缓存。

2. 装饰模式基本的概念

不改变原有代码的基础之上,新增附加功能。
在这里插入图片描述

3. 装饰模式应用场景

  • 多级缓存设计
  • mybatis中一级与二级缓存
  • IO流

4. 装饰者模式定义

(1)抽象组件:定义一个抽象接口,来规范准备附加功能的类
(2)具体组件:将要被附加功能的类,实现抽象构件角色接口
(3)抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口
(4)具体装饰:实现抽象装饰者角色,负责对具体构件添加额外功能。

5. 基于Map手写Jvm内置缓存

package com.tostyle.cache.utils;

import com.alibaba.fastjson.JSONObject;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于Jvm内置缓存
 * @author tostyle
 * 2022/4/8 9:17
 */
public class JvmMapCacheUtils {

    private static Map<String, String> cacheMap = new ConcurrentHashMap<String, String>();


    /**
     * 添加缓存
     * @param key
     * @param object
     */
    public static void putEntity(String key, Object object) {
        cacheMap.put(key, JSONObject.toJSONString(object));
    }

    /**
     * 获取缓存
     * @param key
     * @param t
     * @param <T>
     * @return
     */
    public static <T> T getEntity(String key, Class<T> t) {
        String json = cacheMap.get(key);
        return JSONObject.parseObject(json, t);
    }
}

6. 简单手写一级与二级缓存效果

 /**
     * 原始的二级缓存代码示例
     * @param userId
     * @return
     */
    @GetMapping("/getUserInfoCacheOld")
    public UserInfo getUserInfoCacheOld(Integer userId) {
        //方法名称+参数名称+参数内容
        String key = "getUserInfoCacheOld(Integer)" + userId;
        // 先查询二级缓存
        UserInfo redisUserInfo = redisUtils.getEntity(key, UserInfo.class);
        if (redisUserInfo != null) {
            return redisUserInfo;
        }
        // 再查询一级缓存
        UserInfo jvmUserInfo = JvmMapCacheUtils.getEntity(key, UserInfo.class);
        if (jvmUserInfo != null) {
            //如果有,将数据缓存到redis中
            redisUtils.putEntity(key, jvmUserInfo);
            return jvmUserInfo;
        }
        // 查询数据库
        UserInfo dbUserInfo = userInfoMapper.getUserInfo(userId);
        if(dbUserInfo==null){
            return null;
        }
        JvmMapCacheUtils.putEntity(key,dbUserInfo);
        return dbUserInfo;
    }

7. 基于装饰模式实现多级缓存优化

7.1 一级缓存

public interface ComponentCache {
    <T> T getEntity(String key, Class<T> T, ProceedingJoinPoint joinPoint);
}


package com.tostyle.cache.decorate.impl;

import com.tostyle.cache.decorate.ComponentCache;
import com.tostyle.cache.utils.JvmMapCacheUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

/**
 * 一级缓存
 *
 * @author tostyle
 * 2022/4/8 10:27
 */
@Component
public class JvmComponentCache implements ComponentCache {
    @Override
    public <T> T getEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
        T entity = JvmMapCacheUtils.getEntity(key, t);
        if (entity != null) {
            return entity;
        }
        try {
            //利用Aop调用具体的查询数据方法
            Object dbProceed = joinPoint.proceed();
            if (dbProceed == null) {
                return null;
            }
            JvmMapCacheUtils.putEntity(key, dbProceed);
            return (T) dbProceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
}

7.2 新增二级缓存

package com.tostyle.cache.decorate;

/**
 * @author tostyle
 * 2022/4/8 10:33
 */
public interface AbstractDecorate extends ComponentCache{
}

package com.tostyle.cache.decorate.ext;

import com.tostyle.cache.decorate.AbstractDecorate;
import com.tostyle.cache.decorate.impl.JvmComponentCache;
import com.tostyle.cache.utils.RedisUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 二级缓存
 *
 * @author tostyle
 * 2022/4/8 10:34
 */
@Component
public class RedisDecorate extends JvmComponentCache implements AbstractDecorate {

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public <T> T getEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
        // 先查询二级缓存
        T redisUser = redisUtils.getEntity(key, t);
        if (redisUser != null) {
            return (T) redisUser;
        }
        // 如果一级缓存存在数据
        T jvmUser = super.getEntity(key, t, joinPoint);
        if (jvmUser == null) {
            return null;
        }
        // 将该缓存数据放入到二级缓存中
        redisUtils.putEntity(key, jvmUser);
        return (T) jvmUser;
    }
}

7.3使用一级与二级缓存

package com.tostyle.cache.decorate;

import com.tostyle.cache.decorate.ext.RedisDecorate;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author tostyle
 * 2022/4/8 10:38
 */
@Component
public class MultiCache {

    @Autowired
    private RedisDecorate redisDecorate;

    /**
     * 获取一级和二级缓存
     * @param key
     * @param t
     * @param joinPoint
     * @param <T>
     * @return
     */
    public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
        return redisDecorate.getEntity(key, t, joinPoint);
    }
}

7.4 Aop与自定义注解

package com.tostyle.cache.aop;

import java.lang.annotation.*;

/**
 * 自定义缓存注解
 * @author tostyle
 * 2022/4/8 10:40
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtMultiCache {
}


package com.tostyle.cache.aop;

import com.tostyle.cache.decorate.MultiCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 自定义注解Aop
 */
@Aspect
@Component
@Slf4j
public class ExtAsyncAop {

    @Autowired
    private MultiCache multiCache;


    /**
     * 使用Aop拦截我们的方法上是否使用缓存注解
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(com.tostyle.cache.aop.ExtMultiCache)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 获取目标方法
        Method targetMethod = methodSignature.getMethod();
        // 表示我们目标方法 getUser(Integer userId) {
        String key = targetMethod.getName() + Arrays.toString(targetMethod.getParameterTypes()) +
                Arrays.toString(joinPoint.getArgs());
        return multiCache.getCacheEntity(key, targetMethod.getReturnType(), joinPoint);
    }
}

7.5 测试

    @GetMapping("/getUserInfo")
    @ExtMultiCache
    public UserInfo getUserInfo(Integer userId) {
        return userInfoMapper.getUserInfo(userId);
    }

8.完整代码

https://gitee.com/todostyle/springcloud.git
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值