栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

精讲23种设计模式-基于装饰模式~设计多级缓存框架

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

精讲23种设计模式-基于装饰模式~设计多级缓存框架

文章目录

一、装饰模式

1. 回顾多级缓存基本概念2. 装饰模式基本的概念3. 装饰模式应用场景4. 装饰者模式定义5. 基于Map手写Jvm内置缓存 二、手写一级与二级缓存

2.1. redis工具类2.2. 实体类2.3. 接口2.4. 数据库脚本2.5. 测试案例2.6. 测试效果分享 三、设计多级缓存框架

3.1. 缓存容器抽象3.2. 一级jvm缓存3.3. 二级缓存抽象接口3.4. 新增二级缓存3.5. Aop与自定义注解3.6. 实现二级缓存查询aop拦截3.7. 二级缓存外壳封装3.8. 缓存容器抽象3.9. 请求流程链路3.10. 开源项目
基于装饰模式设计多级缓存

一、装饰模式 1. 回顾多级缓存基本概念

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

2. 装饰模式基本的概念

不改变原有代码的基础之上,新增附加功能

3. 装饰模式应用场景

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

4. 装饰者模式定义

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

5. 基于Map手写Jvm内置缓存
package com.gblfy.utils;

import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

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


@Component
public class JvmMapCacheUtils {

    
    private static Map cacheList = new ConcurrentHashMap();


    
    public static  T getCache(String key, Class t) {
        //    缓存存储对象
        String jsonValue = cacheList.get(key);
        return JSONObject.parseObject(jsonValue, t);
    }

    
    public static void putCache(String key, Object val) {
        String jsonValue = JSONObject.toJSONString(val);
        cacheList.put(key, jsonValue);
    }

    
    public static void removeCacheCacheKey(String cacheKey) {
        cacheList.remove(cacheKey);
    }

    
    public static void updateCacheByCacheKey(String key, Object val) {
        String jsonValue = JSONObject.toJSONString(val);
        cacheList.put(key, jsonValue);
    }

}

二、手写一级与二级缓存 2.1. redis工具类
package com.gblfy.utils;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;


@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 如果key存在的话返回fasle 不存在的话返回true
    public Boolean setNx(String key, String value, Long timeout) {
        Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
        return setIfAbsent;
    }

    
    public void setString(String key, String data, Long timeout) {
        stringRedisTemplate.opsForValue().set(key, data);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    
    public void setString(String key, String data) {
        setString(key, data, null);
    }

    
    public String getString(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value;
    }

    public  T getEntity(String key, Class t) {
        String json = getString(key);
        return JSONObject.parseObject(json, t);
    }

    public void putEntity(String key, Object object) {
        String json = JSONObject.toJSONString(object);
        setString(key, json);
    }

    
    public boolean delKey(String key) {
        return stringRedisTemplate.delete(key);
    }


    public void setList(String key, List listToken) {
        stringRedisTemplate.opsForList().leftPushAll(key, listToken);
    }

    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }
}

2.2. 实体类
package com.gblfy.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("gblfy_user")
public class UserEntity {
    // 主键
    @TableId(value = "user_id", type = IdType.ASSIGN_ID)
    private Integer userId;
    //用户名称
    @TableField("name")
    private String name;
}

2.3. 接口
package com.gblfy.mapper;

import com.baomidou.mybatisplus.core.mapper.baseMapper;
import com.gblfy.entity.UserEntity;
import org.apache.ibatis.annotations.Select;

public interface UserMapper extends baseMapper {
}
2.4. 数据库脚本
drop database  IF EXISTS `design_pattern`;
create database `design_pattern`;
use `design_pattern`;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for gblfy_strategy
-- ----------------------------
DROP TABLE IF EXISTS `gblfy_user`;
CREATE TABLE `gblfy_user` (
  `user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `name` varchar(32) NOT NULL COMMENT '用户名称',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';

INSERT INTO `gblfy_user` VALUES (1, '雨昕');

2.5. 测试案例
package com.gblfy.controller;

import com.gblfy.entity.UserEntity;
import com.gblfy.mapper.UserMapper;
import com.gblfy.utils.JvmMapCacheUtils;
import com.gblfy.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@Slf4j
@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisUtils redisUtils;

    @GetMapping("/getUser")
    public UserEntity getUser(Integer userId) {
        //一级缓存和二级缓存
        //方法名+参数类型+参数
        String key = "getUser(Integer)" + userId;
        //先查询二级缓存
        UserEntity redisUser = redisUtils.getEntity(key, UserEntity.class);
        if (redisUser != null) {
            return redisUser;
        }

        //先查询我们的一级缓存(jvm内置缓存)
        UserEntity jvmUser = JvmMapCacheUtils.getCache(key, UserEntity.class);
        if (jvmUser != null) {

            //当一级缓存不为空时,将内容添加到二级缓存redia中,减少一级缓存的查询压力
            redisUtils.putEntity(key, jvmUser);
            return jvmUser;
        }

        //查询我们的db
        UserEntity dbUser = userMapper.selectById(userId);
        if (dbUser == null) {
            return null;
        }

        //将db查询的内容添加到一级缓存中,减少数据库压力
        JvmMapCacheUtils.putCache(key, dbUser);
        return dbUser;
    }
}

2.6. 测试效果分享

当第一次查询用户数据时流程如下:
先判断redis中是否存在,如果不存在,查询jvm缓存中是否存在
当 jvm缓存中不存在时,查询数据库,再将查询出来的数据添加到jvm缓存中

当第二次查询用户数据时流程如下:
先判断redis中是否存在,如果不存在,查询jvm缓存中是否存在
当 jvm缓存中存在时,先将查询出来的数据添加到redis缓存中,再返回响应缓存数据

当第三次查询用户数据时流程如下:
先判断redis中是否存在,如果存在,直接返回缓存数据

三、设计多级缓存框架 3.1. 缓存容器抽象
package com.gblfy.decoration;

import org.aspectj.lang.ProceedingJoinPoint;


public interface ComponentCache {

    
     T getCacheEntity(String key, Class t, ProceedingJoinPoint joinPoint);
}

3.2. 一级jvm缓存
package com.gblfy.decoration.impl;

import com.gblfy.decoration.ComponentCache;
import com.gblfy.utils.JvmMapCacheUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;


@Component
public class JvmComponentCache implements ComponentCache {

    // @Autowired
    // private UserMapper userMapper;

    @Override
    public  T getCacheEntity(String key, Class t, ProceedingJoinPoint joinPoint) {
        //先查询我们的一级缓存(jvm内置缓存)
        T jvmUser = JvmMapCacheUtils.getCache(key, t);
        if (jvmUser != null) {
            return (T) jvmUser;
        }
        //查询我们的db
        // UserEntity dbUser = userMapper.selectById("1");
        // if (dbUser == null) {
        //     return null;
        // }

        try {
            
            Object resultDb = joinPoint.proceed();
            //将db查询的内容添加到一级缓存中,减少数据库压力
            JvmMapCacheUtils.putCache(key, resultDb);
            return (T) resultDb;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}

3.3. 二级缓存抽象接口
package com.gblfy.decoration;


public interface AbstractDecorate extends ComponentCache {
}
3.4. 新增二级缓存
package com.gblfy.decoration.impl;

import com.gblfy.decoration.AbstractDecorate;
import com.gblfy.utils.RedisUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class RedistDecorate extends JvmComponentCache implements AbstractDecorate {
    @Autowired
    private RedisUtils redisUtils;
    // @Autowired
    // private JvmComponentCache jvmComponentCache;

    @Override
    public  T getCacheEntity(String key, Class t, ProceedingJoinPoint joinPoint) {

        //先查询二级缓存
        T tRedis = redisUtils.getEntity(key, t);
        if (tRedis != null) {
            return (T) tRedis;
        }

        //先查询我们的一级缓存(jvm内置缓存)
        T tJvm = super.getCacheEntity(key, t, joinPoint);

        //如果 extends JvmComponentCache的话可以写成上面super.getCacheEntity(key)这种,前提是装饰类不能new
        // UserEntity jvmUser = jvmComponentCache.getCacheEntity(key);
        if (tJvm == null) {
            return null;
        }

        //当一级缓存不为空时,将内容添加到二级缓存redia中,减少一级缓存的查询压力
        redisUtils.putEntity(key, tJvm);
        return (T) tJvm;
    }
}

3.5. Aop与自定义注解
package com.gblfy.annotation;

import java.lang.annotation.*;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface ExtGblfyCache {
}
3.6. 实现二级缓存查询aop拦截
package com.gblfy.aop;

import com.gblfy.decoration.GblfyCache;
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;


@Aspect
@Component
@Slf4j
public class ExtAsyncAop {
    @Autowired
    private GblfyCache gblfyCache;

    @Around(value = "@annotation(com.gblfy.annotation.ExtGblfyCache)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取目标方法
        Method targetMethod = methodSignature.getMethod();
        //缓存key拼接(方法名+参数类型+参数值)
        String cacheKey = targetMethod.getName() + "," + Arrays.toString(targetMethod.getParameterTypes());
        log.info(">>cacheKey:" + cacheKey);

        // 开始先查询二级缓存是否存在
        return gblfyCache.getCacheEntity(cacheKey, targetMethod.getReturnType(), joinPoint);
        //    这里的泛型T等于方法的返回结果类型简言之targetMethod.getReturnType()
    }
}

3.7. 二级缓存外壳封装
package com.gblfy.decoration;

import com.gblfy.decoration.impl.RedistDecorate;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class GblfyCache {

    @Autowired
    private RedistDecorate redistDecorate;

    public  T getCacheEntity(String key, Class t, ProceedingJoinPoint joinPoint) {
        return redistDecorate.getCacheEntity(key, t, joinPoint);
    }
}

3.8. 缓存容器抽象
package com.gblfy.controller;

import com.gblfy.annotation.ExtGblfyCache;
import com.gblfy.entity.UserEntity;
import com.gblfy.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@Slf4j
@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/getUser")
    @ExtGblfyCache
    public UserEntity getUser(Integer userId) {
        return userMapper.selectById(userId);
    }
 }
3.9. 请求流程链路

当我们访问http://localhost:8080/getUser?userId=1方法时,由于在该方法上有@ExtGblfyCache注解修饰,因此,会被aop拦截。




当地二次查询时,就会只查询redis,查询到后直接返回

3.10. 开源项目

https://gitee.com/gblfy/design-pattern/tree/decoration-mode/

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/769924.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号