日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

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

發布時間:2024/9/27 asp.net 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 精讲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;/*** 基于Map手寫Jvm內置緩存** @Author gblfy* @Date 2022-03-15 21:01**/ @Component public class JvmMapCacheUtils {/*** 并發緩存容器*/private static Map<String, String> cacheList = new ConcurrentHashMap<String, String>();/*** 從本地緩存中根據key獲取值** @param key 緩存key* @param t 返回對象類型* @param <T> 返回對象* @return*/public static <T> T getCache(String key, Class<T> t) {// 緩存存儲對象String jsonValue = cacheList.get(key);return JSONObject.parseObject(jsonValue, t);}/*** 緩存數據到本地jvm中** @param key* @param val*/public static void putCache(String key, Object val) {String jsonValue = JSONObject.toJSONString(val);cacheList.put(key, jsonValue);}/*** 根據緩存key刪除指定緩存數據** @param cacheKey*/public static void removeCacheCacheKey(String cacheKey) {cacheList.remove(cacheKey);}/*** 更新緩存數據** @param key* @param val*/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;/*** redis工具類** @author gblfy* @date 2022-03-15*/ @Component public class RedisUtils {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 如果key存在的話返回fasle 不存在的話返回truepublic 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;}/*** 存放string類型** @param key key* @param data 數據* @param timeout 超時間*/public void setString(String key, String data, Long timeout) {stringRedisTemplate.opsForValue().set(key, data);if (timeout != null) {stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);}}/*** 存放string類型** @param key key* @param data 數據*/public void setString(String key, String data) {setString(key, data, null);}/*** 根據key查詢string類型** @param key* @return*/public String getString(String key) {String value = stringRedisTemplate.opsForValue().get(key);return value;}public <T> T getEntity(String key, Class<T> 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);}/*** 根據對應的key刪除key** @param key*/public boolean delKey(String key) {return stringRedisTemplate.delete(key);}public void setList(String key, List<String> 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<UserEntity> { }
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;/*** 使用裝飾模式~查詢用戶數據** @Author gblfy* @Date 2022-03-15 21:12**/ @Slf4j @RestController public class UserController {@Autowiredprivate UserMapper userMapper;@Autowiredprivate 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;}//查詢我們的dbUserEntity 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;/*** 緩存容器抽象** @Author gblfy* @Date 2022-03-15 21:42**/ public interface ComponentCache {/*** 根據key查詢緩存數據** @param key 緩存的key* @param t 傳入對象參數類型* @param joinPoint 目標方法內容* @param <T> 返回的對象類型* @return*/<T> T getCacheEntity(String key, Class<T> 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;/*** 一級緩存查詢處理類** @Author gblfy* @Date 2022-03-15 21:45**/ @Component public class JvmComponentCache implements ComponentCache {// @Autowired// private UserMapper userMapper;@Overridepublic <T> T getCacheEntity(String key, Class<T> 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 {/*** 當一級緩存不存在時,查詢我們的db,相當于userMapper.selectById(userId)這一行代碼* 1.通過aop直接獲取目標對象的方法* 解析:* 目的:執行joinPoint.proceed();這一行就相當于執行目標方法,為了做成抽象通用的,* 方案:采用aop來實現僅此而已*/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;/*** 二級緩存抽象接口** @author gblfy* @date 2022-03-16*/ 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;/*** 二級緩存查詢處理類** @Author gblfy* @Date 2022-03-15 21:50**/ @Component public class RedistDecorate extends JvmComponentCache implements AbstractDecorate {@Autowiredprivate RedisUtils redisUtils;// @Autowired// private JvmComponentCache jvmComponentCache;@Overridepublic <T> T getCacheEntity(String key, Class<T> 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.*;/*** 二級緩存查詢aop攔截** @author gblfy* @date 2022-03-15*/ @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;/*** 實現二級緩存查詢aop攔截* 自定義ExtGblfyCache注解** @author gblfy* @date 2022-03-15*/ @Aspect @Component @Slf4j public class ExtAsyncAop {@Autowiredprivate 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;/*** 二級緩存外殼封裝** @Author gblfy* @Date 2022-03-15 22:01**/ @Component public class GblfyCache {@Autowiredprivate RedistDecorate redistDecorate;public <T> T getCacheEntity(String key, Class<T> 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;/*** 使用裝飾模式~查詢用戶數據** @Author gblfy* @Date 2022-03-15 21:12**/ @Slf4j @RestController public class UserController {@Autowiredprivate UserMapper userMapper;@GetMapping("/getUser")@ExtGblfyCachepublic 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/

總結

以上是生活随笔為你收集整理的精讲23种设计模式-基于装饰模式~设计多级缓存框架的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。