当前位置: 首页 > news >正文

phpwind网站如何做内网网站

phpwind网站,如何做内网网站,网络销售 市场推广,安装网站程序的流程目录 一、整体方案说明 1.1 需求说明 1.2 整体方案实现组件结构图 二、Caffeine缓存实现 2.1 组件说明 2.2 组件结构图 2.3 组件Maven依赖 2.4 组件功能实现源码 2.4.1 CaffeineCacheManager扩展实现 2.4.2 CaffeineConfiguration配置类实现 2.4.3 涉及其他组件的类 … 目录 一、整体方案说明 1.1 需求说明 1.2 整体方案实现组件结构图 二、Caffeine缓存实现 2.1 组件说明 2.2 组件结构图 2.3 组件Maven依赖 2.4 组件功能实现源码 2.4.1 CaffeineCacheManager扩展实现 2.4.2 CaffeineConfiguration配置类实现 2.4.3 涉及其他组件的类 2.4.3.1 缓存过期时间通用属性类 2.4.3.2 缓存配置类 三、Redis缓存实现 3.1 组件说明 3.2 组件结构图 3.3 组件Maven依赖 3.4 组件功能实现源码 3.4.1 实现Redis缓存功能 3.4.1.1 RedisCacheManager 类扩展实现 3.4.1.2 RedisBitMapUtils工具类 3.4.1.3 CacheRedisConfiguration 缓存配置类实现 3.4.1.4 EnableKuaFuCloudRedis Redis启动注解 3.4.2 实现Spring Session的Redis缓存 3.4.2.1 HttpSessionIdResolver SessionId 解析类扩展 3.4.2.2 RedisSessionSharingConfiguration 启动配置类 3.4.2.3 注解生效条件类RedisSessionSharingCondition 3.4.2.4 ConditionalOnRedisSessionSharing 注解类 四、JetCache缓存框架整合 4.1 组件说明 4.2 组件结构图 4.3 组件Maven依赖 4.4 组件功能实现源码 4.4.1 Mybatis二级缓存扩展 4.4.1.1 ExtMybatisCache 实现类 4.4.2 Spring Cache缓存扩展 4.4.2.1 JetCache缓存工厂类JetCacheCreateCacheFactory 4.4.2.2 Spring Cache扩展实现类JetCacheSpringCache 4.4.2.3 Spring CacheManager扩展实现类JetCacheSpringCacheManager 4.4.2.4 自定义缓存管理类KuaFuCloudCacheManager 4.4.3 Stamp 缓存签章接口封装 4.4.3.1 StampManager 缓存签章接口 4.4.3.2 抽象Stamp签章管理类AbstractStampManager 4.4.3.3 抽象缓存计数类AbstractCountStampManager 4.4.4 JetCache直接使用封装 4.4.4.1 JetCache单例工具类JetCacheUtils 4.4.5 JetCache注解方式启动封装 4.4.5.1 JetCache加载配置类JetCacheConfiguration 4.4.5.2 JetCache手动启动注解 4.4.6 Jpa Hibernate二级缓存扩展 4.4.6.1 DomainDataStorageAccess 接口实现 4.4.6.2 RegionFactoryTemplate 模板扩展 五、缓存 Start 启动组件封装 5.1 组件说明 5.2 组件结构图 5.3 组件Maven依赖 5.4 组件功能实现源码 5.4.1 组件启动类 5.4.2 SPI加载机制配置文件 一、整体方案说明 1.1 需求说明 1、基于JetCache 缓存框架整合Caffeine和Redis实现一级进程级、二级远程分布式缓存。 2、基于该缓存方案实现对Spring Cache缓存的扩展。 3、实现对Jpa Hibernate、Mybatis两款ORM框架二级缓存的扩展。 4、基于Redis缓存实现对SpringBoot Session 的共享会话。 5、Stamp 签章缓存实现。 6、组件最终封装成SpringBoot start 组件方便一键使用。 1.2 整体方案实现组件结构图 二、Caffeine缓存实现 2.1 组件说明 封装成cache-caffeine-sdk组件该组件封装了Caffeine缓存的相关实现扩展最终通过CaffeineConfiguration配置启动。 2.2 组件结构图 2.3 组件Maven依赖 dependenciesdependencygroupId${project.groupId}/groupIdartifactIdcache-core/artifactId/dependencydependencygroupIdcom.github.ben-manes.caffeine/groupIdartifactIdcaffeine/artifactId/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactId/dependency/dependencies 2.4 组件功能实现源码 2.4.1 CaffeineCacheManager扩展实现 package cn.kuafu.cloud.base.cache.caffeine.enhance;import cn.kuafu.cloud.base.assistant.core.definition.constants.SymbolConstants; import cn.kuafu.cloud.base.cache.core.properties.Expire; import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.caffeine.CaffeineCacheManager;import java.util.Map;/*** author ningzhaosheng* date 2022/5/13 12:11:55* description 扩展的 CaffeineCacheManager* 用于支持 Caffeine 缓存可以针对实体进行单独的过期时间设定*/ public class KuaFuCloudCaffeineCacheManager extends CaffeineCacheManager {private static final Logger log LoggerFactory.getLogger(KuaFuCloudCaffeineCacheManager.class);private final CacheProperties cacheProperties;// 构造方法public KuaFuCloudCaffeineCacheManager(CacheProperties cacheProperties) {this.cacheProperties cacheProperties;this.setAllowNullValues(cacheProperties.getAllowNullValues());}// 构造方法public KuaFuCloudCaffeineCacheManager(CacheProperties cacheProperties, String... cacheNames) {super(cacheNames);this.cacheProperties cacheProperties;this.setAllowNullValues(cacheProperties.getAllowNullValues());}// 覆盖CaffeineCacheManager 的 createNativeCaffeineCache方法实现Caffeine创建本地缓存方法// 主要支持Duration 过期时间配置Overrideprotected CacheObject, Object createNativeCaffeineCache(String name) {MapString, Expire expires cacheProperties.getExpires();if (MapUtils.isNotEmpty(expires)) {String key StringUtils.replace(name, SymbolConstants.COLON, cacheProperties.getSeparator());if(expires.containsKey(key)) {Expire expire expires.get(key);log.debug([KuaFuCloud-Base-Cache] |- CACHE - Caffeine cache [{}] is setted to use CUSTEM exprie., name);return Caffeine.newBuilder().expireAfterWrite(expire.getDuration(), expire.getUnit()).build();}}return super.createNativeCaffeineCache(name);} } 2.4.2 CaffeineConfiguration配置类实现 package cn.kuafu.cloud.base.cache.caffeine.configuration;import cn.kuafu.cloud.base.cache.caffeine.enhance.KuaFuCloudCaffeineCacheManager; import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import com.github.benmanes.caffeine.cache.Caffeine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** author ningzhaosheng* date 2022/5/13 14:15:20* description Caffeine配置*/ Configuration(proxyBeanMethods false) public class CaffeineConfiguration {private static final Logger log LoggerFactory.getLogger(CaffeineConfiguration.class);Autowiredprivate CacheProperties cacheProperties;PostConstructpublic void postConstruct() {log.debug([KuaFuCloud-Base-Cache] |- SDK [Cache Caffeine] Auto Configure.);}// 注入 Caffeine缓存类Beanpublic CaffeineObject, Object caffeine() {CaffeineObject, Object caffeine Caffeine.newBuilder().expireAfterWrite(cacheProperties.getDuration(), cacheProperties.getUnit());log.trace([KuaFuCloud-Base-Cache] |- Bean [Caffeine] Auto Configure.);return caffeine;}// 注入 CaffeineCacheManager 类BeanConditionalOnMissingBean(CaffeineCacheManager.class)public CaffeineCacheManager caffeineCacheManager(CaffeineObject, Object caffeine) {KuaFuCloudCaffeineCacheManager caffeineCacheManager new KuaFuCloudCaffeineCacheManager(cacheProperties);caffeineCacheManager.setCaffeine(caffeine);log.trace([KuaFuCloud-Base-Cache] |- Bean [Caffeine Cache Manager] Auto Configure.);return caffeineCacheManager;} } 2.4.3 涉及其他组件的类 2.4.3.1 缓存过期时间通用属性类 package cn.kuafu.cloud.base.cache.core.properties;import org.apache.commons.lang3.ObjectUtils;import java.time.Duration; import java.util.concurrent.TimeUnit;/*** author ningzhaosheng* date 2022/5/12 22:00:42* description 缓存过期时间通用属性*/ public class Expire {/*** 统一缓存时长默认1*/private Long duration 1L;/*** 统一缓存时长单位默认小时。*/private TimeUnit unit TimeUnit.HOURS;/*** Redis缓存TTL设置默认1小时单位小时* p* 使用Duration类型配置参数形式如下* ?ns //纳秒* ?us //微秒* ?ms //毫秒* ?s //秒* ?m //分* ?h //小时* ?d //天*/private Duration ttl;public Long getDuration() {return duration;}public void setDuration(Long duration) {this.duration duration;}public TimeUnit getUnit() {return unit;}public void setUnit(TimeUnit unit) {this.unit unit;}public Duration getTtl() {if (ObjectUtils.isEmpty(this.ttl)) {this.ttl convertToDuration(this.duration, this.unit);}return ttl;}private Duration convertToDuration(Long duration, TimeUnit timeUnit) {switch (timeUnit) {case DAYS:return Duration.ofDays(duration);case HOURS:return Duration.ofHours(duration);case SECONDS:return Duration.ofSeconds(duration);default:return Duration.ofMinutes(duration);}} } 2.4.3.2 缓存配置类 package cn.kuafu.cloud.base.cache.core.properties;import cn.kuafu.cloud.base.cache.core.constants.CacheConstants; import com.google.common.base.MoreObjects; import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.HashMap; import java.util.Map;/*** author ningzhaosheng* date 2022/5/13 11:55:06* description 缓存配置属性*/ ConfigurationProperties(prefix CacheConstants.PROPERTY_PREFIX_CACHE) public class CacheProperties extends Expire {/*** 是否允许存储空值*/private Boolean allowNullValues true;/*** 缓存名称转换分割符。默认值-* p* 默认缓存名称采用 Redis Key 格式使用 : 分割使用 : 分割的字符串作为Map的Key:会丢失。* 指定一个分隔符用于 : 分割符的转换*/private String separator -;/*** 针对不同实体单独设置的过期时间如果不设置则统一使用默认时间。*/private MapString, Expire expires new HashMap();public Boolean getAllowNullValues() {return allowNullValues;}public void setAllowNullValues(Boolean allowNullValues) {this.allowNullValues allowNullValues;}public MapString, Expire getExpires() {return expires;}public void setExpires(MapString, Expire expires) {this.expires expires;}public String getSeparator() {return separator;}public void setSeparator(String separator) {this.separator separator;}Overridepublic String toString() {return MoreObjects.toStringHelper(this).add(allowNullValues, allowNullValues).add(separator, separator).toString();} } 三、Redis缓存实现 3.1 组件说明 封装成cache-redis-sdk组件。 1、该组件封装了Redis缓存的相关实现扩展最终通过CacheRedisConfiguration配置启动。 2、基于Redis实现SpringBoot Session共享支持通过条件判断和注解形式选择是否开启session共享 3.2 组件结构图 3.3 组件Maven依赖 dependenciesdependencygroupId${project.groupId}/groupIdartifactIdcache-core/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis-reactive/artifactId/dependencydependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdscopecompile/scopeoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webflux/artifactIdscopecompile/scopeoptionaltrue/optional/dependency/dependencies 3.4 组件功能实现源码 3.4.1 实现Redis缓存功能 3.4.1.1 RedisCacheManager 类扩展实现 package cn.kuafu.cloud.base.cache.redis.enhance;import cn.kuafu.cloud.base.assistant.core.definition.constants.SymbolConstants; import cn.kuafu.cloud.base.cache.core.properties.Expire; import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter;import java.util.Map;/*** author ningzhaosheng* date 2022/5/13 12:07:49* description 扩展的RedisCacheManager* 用于支持 Redis 缓存可以针对实体进行单独的过期时间设定*/ public class KuaFuCloudRedisCacheManager extends RedisCacheManager {private static final Logger log LoggerFactory.getLogger(KuaFuCloudRedisCacheManager.class);private CacheProperties cacheProperties;// 构造函数public KuaFuCloudRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, CacheProperties cacheProperties) {super(cacheWriter, defaultCacheConfiguration);this.cacheProperties cacheProperties;}// 构造函数public KuaFuCloudRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, CacheProperties cacheProperties, String... initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, initialCacheNames);this.cacheProperties cacheProperties;}// 构造函数public KuaFuCloudRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, CacheProperties cacheProperties, String... initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);this.cacheProperties cacheProperties;}// 构造函数public KuaFuCloudRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, MapString, RedisCacheConfiguration initialCacheConfigurations, CacheProperties cacheProperties) {super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);this.cacheProperties cacheProperties;}// 构造函数public KuaFuCloudRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, MapString, RedisCacheConfiguration initialCacheConfigurations, boolean allowInFlightCacheCreation) {super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);}// 覆盖实现 RedisCacheManager 类的 createRedisCache方法加入自定义缓存过期时间策略Overrideprotected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {MapString, Expire expires cacheProperties.getExpires();if (MapUtils.isNotEmpty(expires)) {String key StringUtils.replace(name, SymbolConstants.COLON, cacheProperties.getSeparator());if (expires.containsKey(key)) {Expire expire expires.get(key);log.debug([KuaFuCloud-Base-Cache] |- CACHE - Redis cache [{}] is setted to use CUSTEM exprie., name);cacheConfig cacheConfig.entryTtl(expire.getTtl());}}return super.createRedisCache(name, cacheConfig);} } 3.4.1.2 RedisBitMapUtils工具类 package cn.kuafu.cloud.base.cache.redis.util;import com.google.common.hash.Funnels; import com.google.common.hash.Hashing; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component;import java.nio.charset.StandardCharsets;/*** author ningzhaosheng* date 2023/2/16 16:24:06* description pDescription: Redis BitMap 工具类 /p* p* · Redis的Bitmaps这个“数据结构”可以实现对位的操作。Bitmaps本身不是一种数据结构实际上就是字符串但是它可以对字符串的位进行操作* · 可把Bitmaps想象成一个以位为单位数组数组中的每个单元只能存0或者1数组的下标在bitmaps中叫做偏移量* · 单个bitmaps的最大长度是512MB即2^32个比特位* p* bitmaps的最大优势是节省存储空间。比如在一个以自增id代表不同用户的系统中我们只需要512MB空间就可以记录40亿用户的某个单一信息相比mysql节省了大量的空间* p* · 有两种类型的位操作一类是对特定bit位的操作比如设置/获取某个特定比特位的值。另一类是批量bit位操作例如在给定范围内统计为1的比特位个数* p* 1.1 优点* p* 节省空间通过一个bit位来表示某个元素对应的值或者状态其中key就是对应元素的值。实际上8个bit可以组成一个Byte所以是及其节省空间的。* p* 效率高setbit和getbit的时间复杂度都是O(1)其他位运算效率也高。* p* 1.2 缺点* 本质上位只有0和1的区别所以用位做业务数据记录就不需要在意value的值。* see a herfhttps://www.jianshu.com/p/305e65de1b13/a*/ Component public class RedisBitMapUtils {private static StringRedisTemplate stringRedisTemplate;AutowiredQualifier(value stringRedisTemplate)public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {RedisBitMapUtils.stringRedisTemplate stringRedisTemplate;}/*** 计算 Hash 值** param key bitmap结构的key* return Hash 值*/private static long hash(String key) {return Math.abs(Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(StandardCharsets.UTF_8)).asInt());}/*** 设置与 param 对应的二进制位的值{param param}会经过hash计算进行存储。** param key bitmap数据结构的key* param param 要设置偏移的key该key会经过hash运算。* param value true即该位设置为1否则设置为0* return 返回设置该value之前的值。*/public static Boolean setBit(String key, String param, boolean value) {return stringRedisTemplate.opsForValue().setBit(key, hash(param), value);}/*** 查询与指定 param 对应二进制位的值{param param}会经过hash计算进行存储。** param key bitmap结构的key* param param 要移除偏移的key该key会经过hash运算。* return 若偏移位上的值为1那么返回true。*/public static boolean getBit(String key, String param) {return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().getBit(key, hash(param)));}/*** 将指定offset偏移量的值设置为1** param key bitmap结构的key* param offset 指定的偏移量。* param value true即该位设置为1否则设置为0* return 返回设置该value之前的值。*/public static Boolean setBit(String key, Long offset, boolean value) {return stringRedisTemplate.opsForValue().setBit(key, offset, value);}/*** 获取指定 offset 偏移量的值** param key bitmap结构的key* param offset 指定的偏移量。* return 若偏移位上的值为 1那么返回true。*/public static Boolean getBit(String key, long offset) {return stringRedisTemplate.opsForValue().getBit(key, offset);}/*** 统计对应的bitmap上value为1的数量** param key bitmap的key* return value等于1的数量*/public static Long bitCount(String key) {return stringRedisTemplate.execute((RedisCallbackLong) connection - connection.stringCommands().bitCount(key.getBytes(StandardCharsets.UTF_8)));}/*** 统计指定范围中value为1的数量** param key bitMap中的key* param start 该参数的单位是byte1byte8bit{code setBit(key,7,true);}进行存储时单位是bit。那么只需要统计[0,1]便可以统计到上述set的值。* param end 该参数的单位是byte。* return 在指定范围[start*8,end*8]内所有value1的数量*/public static Long bitCount(String key, int start, int end) {return stringRedisTemplate.execute((RedisCallbackLong) connection - connection.stringCommands().bitCount(key.getBytes(), start, end));}/*** 对一个或多个保存二进制的字符串key进行元操作并将结果保存到saveKey上。* p* bitop and saveKey key [key...]对一个或多个key逻辑并结果保存到saveKey。* bitop or saveKey key [key...]对一个或多个key逻辑或结果保存到saveKey。* bitop xor saveKey key [key...]对一个或多个key逻辑异或结果保存到saveKey。* bitop xor saveKey key对一个或多个key逻辑非结果保存到saveKey。* p** param op 元操作类型* param saveKey 元操作后将结果保存到saveKey所在的结构中。* param destKey 需要进行元操作的类型。* return 1返回元操作值。*/public static Long bitOp(RedisStringCommands.BitOperation op, String saveKey, String... destKey) {byte[][] bytes new byte[destKey.length][];for (int i 0; i destKey.length; i) {bytes[i] destKey[i].getBytes();}return stringRedisTemplate.execute((RedisCallbackLong) connection - connection.stringCommands().bitOp(op, saveKey.getBytes(), bytes));}/*** 对一个或多个保存二进制的字符串key进行元操作并将结果保存到saveKey上并返回统计之后的结果。** param op 元操作类型* param saveKey 元操作后将结果保存到saveKey所在的结构中。* param destKey 需要进行元操作的类型。* return 返回saveKey结构上value1的所有数量值。*/public static Long bitOpResult(RedisStringCommands.BitOperation op, String saveKey, String... destKey) {bitOp(op, saveKey, destKey);return bitCount(saveKey);} } 3.4.1.3 CacheRedisConfiguration 缓存配置类实现 package cn.kuafu.cloud.base.cache.redis.configuration;import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import cn.kuafu.cloud.base.cache.redis.enhance.KuaFuCloudRedisCacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;import javax.annotation.PostConstruct;/*** author ningzhaosheng* date 2022/5/13 14:26:19* description Redis缓存配置*/ Configuration(proxyBeanMethods false) Import(RedisSessionSharingConfiguration.class) public class CacheRedisConfiguration {private static final Logger log LoggerFactory.getLogger(CacheRedisConfiguration.class);PostConstructpublic void postConstruct() {log.debug([KuaFuCloud-Base-Cache] |- SDK [Cache Redis] Auto Configure.);}private RedisSerializerString keySerializer() {return new StringRedisSerializer();}private RedisSerializerObject valueSerializer() {return new Jackson2JsonRedisSerializer(Object.class);}/*** 重新配置一个RedisTemplate** return {link RedisTemplate}*/Bean(name redisTemplate)ConditionalOnMissingBeanpublic RedisTemplateObject, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateObject, Object redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(keySerializer());redisTemplate.setHashKeySerializer(keySerializer());redisTemplate.setValueSerializer(valueSerializer());redisTemplate.setHashValueSerializer(valueSerializer());redisTemplate.setDefaultSerializer(valueSerializer());redisTemplate.afterPropertiesSet();log.trace([KuaFuCloud-Base-Cache] |- Bean [Redis Template] Auto Configure.);return redisTemplate;}// 注入一个 StringRedisTemplate Bean(name stringRedisTemplate)ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate stringRedisTemplate new StringRedisTemplate();stringRedisTemplate.setConnectionFactory(redisConnectionFactory);stringRedisTemplate.afterPropertiesSet();log.trace([KuaFuCloud-Base-Cache] |- Bean [String Redis Template] Auto Configure.);return stringRedisTemplate;}// 注入一个Redis 消息的核心监听容器Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer container new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);log.trace([KuaFuCloud-Base-Cache] |- Bean [Redis Message Listener Container] Auto Configure.);return container;}// 注入Redis 缓存管理类Beanpublic RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory, CacheProperties cacheProperties) {RedisCacheWriter redisCacheWriter RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);// 注意这里 RedisCacheConfiguration 每一个方法调用之后都会返回一个新的 RedisCacheConfiguration 对象所以要注意对象的引用关系。RedisCacheConfiguration redisCacheConfiguration RedisCacheConfiguration.defaultCacheConfig().entryTtl(cacheProperties.getTtl());boolean allowNullValues cacheProperties.getAllowNullValues();if (!allowNullValues) {// 注意这里 RedisCacheConfiguration 每一个方法调用之后都会返回一个新的 RedisCacheConfiguration 对象所以要注意对象的引用关系。redisCacheConfiguration redisCacheConfiguration.disableCachingNullValues();}KuaFuCloudRedisCacheManager kuaFuCloudRedisCacheManager new KuaFuCloudRedisCacheManager(redisCacheWriter, redisCacheConfiguration, cacheProperties);kuaFuCloudRedisCacheManager.setTransactionAware(false);kuaFuCloudRedisCacheManager.afterPropertiesSet();log.trace([KuaFuCloud-Base-Cache] |- Bean [Redis Cache Manager] Auto Configure.);return kuaFuCloudRedisCacheManager;}// 扫描 RedisBitMapUtils 工具类Configuration(proxyBeanMethods false)ComponentScan({cn.kuafu.cloud.base.cache.redis.util})static class RedisUtilsConfiguration {PostConstructpublic void postConstruct() {log.debug([KuaFuCloud-Base-Cache] |- SDK [Cache Redis Utils] Auto Configure.);}} }3.4.1.4 EnableKuaFuCloudRedis Redis启动注解 package cn.kuafu.cloud.base.cache.redis.annotation;import cn.kuafu.cloud.base.cache.redis.configuration.CacheRedisConfiguration; import org.springframework.context.annotation.Import;import java.lang.annotation.*;/*** author ningzhaosheng* date 2023/2/16 16:12:04* description 开启平台Redis*/ Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Import(CacheRedisConfiguration.class) public interface EnableKuaFuCloudRedis { } 3.4.2 实现Spring Session的Redis缓存 在Web应用中Session是一种用来存储用户状态信息的机制。用户通过登录认证后服务器会为每个用户生成一个唯一的Session ID并将这个Session ID与用户的状态信息关联起来然后将Session ID返回给客户端保存在Cookie中。客户端在后续的请求中通过Cookie将Session ID发送给服务器服务器根据Session ID找到对应的用户状态信息。 通常情况下每个Web应用都会有自己的Session管理机制这意味着不同的应用之间的Session是相互隔离的。但在某些场景下我们希望多个Web应用之间可以实现Session共享即一个Web应用生成的Session可以在另一个Web应用中被识别和使用。这样可以实现用户在不同的应用之间的无缝切换提升用户体验。 Spring Session通过替换底层的HttpSession实现类将Session存储到Redis中。具体而言当用户访问Spring Boot应用时Spring Session会将生成的Session ID写入Cookie然后将Session的状态信息存储到Redis中。当用户发送后续的请求时Spring Session会根据Cookie中的Session ID从Redis中读取Session的状态信息并将其保存在HttpSession对象中供应用程序使用。 3.4.2.1 HttpSessionIdResolver SessionId 解析类扩展 package cn.kuafu.cloud.base.cache.redis.session;import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.session.web.http.CookieHttpSessionIdResolver; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer; import org.springframework.session.web.http.HttpSessionIdResolver;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.List;/*** author ningzhaosheng* date 2023/2/16 16:28:03* description 扩展的 HttpSessionIdResolver, 以同时支持页面和接口的 Session 共享*/ public class KuaFuCloudHttpSessionIdResolver implements HttpSessionIdResolver {private static final Logger log LoggerFactory.getLogger(KuaFuCloudHttpSessionIdResolver.class);private static final String WRITTEN_SESSION_ID_ATTR CookieHttpSessionIdResolver.class.getName().concat(.WRITTEN_SESSION_ID_ATTR);private final String headerName;private CookieSerializer cookieSerializer new DefaultCookieSerializer();/*** 构造函数** param headerName*/public KuaFuCloudHttpSessionIdResolver(String headerName) {if (StringUtils.isBlank(headerName)) {throw new IllegalArgumentException(headerName cannot be null);}this.headerName headerName;}private String resolveHeaderSessionId(HttpServletRequest request) {String headerValue request.getHeader(this.headerName);if (StringUtils.isNotBlank(headerValue)) {log.debug([KuaFuCloud-Base-Cache] |- Resolve http session id [{}] from header in request [{}], headerValue, request.getRequestURI());return headerValue;}return null;}private ListString resolveHeaderSessionIds(HttpServletRequest request) {String id resolveHeaderSessionId(request);return StringUtils.isNotBlank(id) ? Collections.singletonList(id) : Collections.emptyList();}/*** 解析与当前请求相关联的sessionId。sessionId可能来自Cookie或请求头。* 实现该方法替换Cookies序列化方式** param request* return*/Overridepublic ListString resolveSessionIds(HttpServletRequest request) {ListString idsInHeader resolveHeaderSessionIds(request);if (CollectionUtils.isNotEmpty(idsInHeader)) {return idsInHeader;} else {return this.cookieSerializer.readCookieValues(request);}}private void changeSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {this.cookieSerializer.writeCookieValue(new CookieSerializer.CookieValue(request, response, sessionId));response.setHeader(this.headerName, sessionId);}/*** 将给定的sessionId发送给客户端。* 这个方法是在创建一个新session时被调用并告知客户端新sessionId是什么。** param request* param response* param sessionId*/Overridepublic void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {if (sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) {return;}String id sessionId;String kuafuCloudSessionId resolveHeaderSessionId(request);if (StringUtils.isNotBlank(kuafuCloudSessionId)) {id kuafuCloudSessionId;}request.setAttribute(WRITTEN_SESSION_ID_ATTR, id);changeSessionId(request, response, id);}/*** 指示客户端结束当前session。当session无效时调用此方法并* 应通知客户端sessionId不再有效。比如它可能删除一个包含sessionId的Cookie* 或者设置一个HTTP响应头其值为空就表示客户端不再提交sessionId** param request* param response*/Overridepublic void expireSession(HttpServletRequest request, HttpServletResponse response) {changeSessionId(request, response, );}/*** Sets the {link CookieSerializer} to be used.** param cookieSerializer the cookieSerializer to set. Cannot be null.*/public void setCookieSerializer(CookieSerializer cookieSerializer) {if (cookieSerializer null) {throw new IllegalArgumentException(cookieSerializer cannot be null);}this.cookieSerializer cookieSerializer;} } 3.4.2.2 RedisSessionSharingConfiguration 启动配置类 package cn.kuafu.cloud.base.cache.redis.configuration;import cn.kuafu.cloud.base.cache.redis.annotation.ConditionalOnRedisSessionSharing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.FlushMode; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer;import javax.annotation.PostConstruct;/*** author ningzhaosheng* date 2022/6/11 18:18:35* description 基于 Redis 的 Session 共享配置* EnableRedisHttpSession 注解开启SpringBoot Session* ConditionalOnRedisSessionSharing 自定义条件注解满足条件该配置类才启动加载*/ Configuration(proxyBeanMethods false) ConditionalOnRedisSessionSharing public class RedisSessionSharingConfiguration {private static final Logger log LoggerFactory.getLogger(RedisSessionSharingConfiguration.class);PostConstructpublic void postConstruct() {log.debug([KuaFuCloud-Base-Cache] |- SDK [Cache Redis Session Sharing] Auto Configure.);}Configuration(proxyBeanMethods false)ConditionalOnWebApplication(type ConditionalOnWebApplication.Type.SERVLET)EnableRedisHttpSession(flushMode FlushMode.IMMEDIATE)static class KuaFuCloudRedisHttpSessionConfiguration {/*** 注入CookieSerializer 实现Cookies 序列化* return*/Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer cookieSerializer new DefaultCookieSerializer();cookieSerializer.setUseHttpOnlyCookie(false);cookieSerializer.setSameSite(null);cookieSerializer.setCookiePath(/);cookieSerializer.setDomainNamePattern(^.?\\.(\\w\\.[a-z])$);log.trace([KuaFuCloud-Base-Cache] |- Bean [Cookie Serializer] Auto Configure.);return cookieSerializer;}} } 3.4.2.3 注解生效条件类RedisSessionSharingCondition package cn.kuafu.cloud.base.cache.redis.condition;import cn.kuafu.cloud.base.assistant.core.context.PropertyFinder; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;/*** author ningzhaosheng* date 2022/6/11 18:16:11* description 开启基于 Redis 的 Session 共享条件*/ public class RedisSessionSharingCondition implements Condition {private static final Logger log LoggerFactory.getLogger(RedisSessionSharingCondition.class);SuppressWarnings(NullableProblems)Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {String property PropertyFinder.getSessionStoreType(conditionContext.getEnvironment());boolean result StringUtils.isNotBlank(property) StringUtils.equalsIgnoreCase(property, redis);log.debug([KuaFuCloud-Base-Cache] |- Condition [Redis Session Sharing] value is [{}], result);return result;} }3.4.2.4 ConditionalOnRedisSessionSharing 注解类 package cn.kuafu.cloud.base.cache.redis.annotation;import cn.kuafu.cloud.base.cache.redis.condition.RedisSessionSharingCondition; import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;/*** author ningzhaosheng* date 2022/6/11 18:15:21* description 基于 Redis Session 共享条件注解*/ Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented Conditional(RedisSessionSharingCondition.class) public interface ConditionalOnRedisSessionSharing { }四、JetCache缓存框架整合 4.1 组件说明 封装成cache-jetcache-sdk组件。 1、集成Caffeine一级缓存 2、集成Redis二级缓存 3、扩展Spring Cache 缓存 4、扩展Mybatis 二级缓存 5、Stamp 签章缓存支持 4.2 组件结构图 4.3 组件Maven依赖 dependenciesdependencygroupId${project.groupId}/groupIdartifactIdassistant-spring-boot-starter/artifactId/dependencydependencygroupId${project.groupId}/groupIdartifactIdcache-core/artifactId/dependencydependencygroupId${project.groupId}/groupIdartifactIdcache-redis-sdk/artifactId/dependencydependencygroupId${project.groupId}/groupIdartifactIdcache-caffeine-sdk/artifactId/dependencydependencygroupIdcom.alicp.jetcache/groupIdartifactIdjetcache-starter-redis-lettuce/artifactId/dependencydependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdscopecompile/scopeoptionaltrue/optional/dependency/dependencies 4.4 组件功能实现源码 4.4.1 Mybatis二级缓存扩展 4.4.1.1 ExtMybatisCache 实现类 package cn.kuafu.cloud.base.cache.jetcache.enhance;import cn.hutool.extra.spring.SpringUtil; import org.apache.ibatis.cache.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.util.concurrent.atomic.AtomicInteger;/*** author ningzhaosheng* date 2022/5/13 9:57:24* description 扩展的Mybatis二级缓存*/ public class ExtMybatisCache implements Cache {private static final Logger log LoggerFactory.getLogger(ExtMybatisCache.class);private final String id;private final com.alicp.jetcache.CacheObject, Object cache;private final AtomicInteger counter new AtomicInteger(0);public ExtMybatisCache(String id) {this.id id;JetCacheCreateCacheFactory jetCacheCreateCacheFactory SpringUtil.getBean(jetCacheCreateCacheFactory);this.cache jetCacheCreateCacheFactory.create(this.id);}/*** 获取缓存对象标识* return*/Overridepublic String getId() {return this.id;}/*** 往缓存中放数据* param key* param value*/Overridepublic void putObject(Object key, Object value) {cache.put(key, value);counter.incrementAndGet();log.debug([KuaFuCloud-Base-Cache] |- CACHE - Put data into Mybatis Cache, with key: [{}], key);}/*** 获取缓存数据* param key* return*/Overridepublic Object getObject(Object key) {Object obj cache.get(key);log.debug([KuaFuCloud-Base-Cache] |- CACHE - Get data from Mybatis Cache, with key: [{}], key);return obj;}/*** 删除缓存数据* param key* return*/Overridepublic Object removeObject(Object key) {Object obj cache.remove(key);counter.decrementAndGet();log.debug([KuaFuCloud-Base-Cache] |- CACHE - Remove data from Mybatis Cache, with key: [{}], key);return obj;}/*** 清空缓存数据*/Overridepublic void clear() {cache.close();log.debug([KuaFuCloud-Base-Cache] |- CACHE - Clear Mybatis Cache.);}/*** 获取缓存数据的数量* return*/Overridepublic int getSize() {return counter.get();} } 4.4.2 Spring Cache缓存扩展 4.4.2.1 JetCache缓存工厂类JetCacheCreateCacheFactory package cn.kuafu.cloud.base.cache.jetcache.enhance;import com.alicp.jetcache.Cache; import com.alicp.jetcache.CacheManager; import com.alicp.jetcache.anno.CacheType; import com.alicp.jetcache.template.QuickConfig; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils;import java.time.Duration;/*** author ningzhaosheng* date 2022/8/23 16:58:02* description JetCache 手动创建Cache 工厂*/ public class JetCacheCreateCacheFactory {private final CacheManager cacheManager;public JetCacheCreateCacheFactory(CacheManager cacheManager) {this.cacheManager cacheManager;}public K, V CacheK, V create(String name) {return create(name, Duration.ofHours(2L));}public K, V CacheK, V create(String name, Duration expire) {return create(name, expire, true);}public K, V CacheK, V create(String name, Duration expire, Boolean cacheNullValue) {return create(name, expire, cacheNullValue, null);}public K, V CacheK, V create(String name, Duration expire, Boolean cacheNullValue, Boolean syncLocal) {return create(name, CacheType.BOTH, expire, cacheNullValue, syncLocal);}public K, V CacheK, V create(String name, CacheType cacheType) {return create(name, cacheType, null);}public K, V CacheK, V create(String name, CacheType cacheType, Duration expire) {return create(name, cacheType, expire, true);}public K, V CacheK, V create(String name, CacheType cacheType, Duration expire, Boolean cacheNullValue) {return create(name, cacheType, expire, cacheNullValue, null);}public K, V CacheK, V create(String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal) {return create(null, name, cacheType, expire, cacheNullValue, syncLocal);}public K, V CacheK, V create(String area, String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal) {return create(area, name, cacheType, expire, cacheNullValue, syncLocal, null);}public K, V CacheK, V create(String area, String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal, Duration localExpire) {return create(area, name, cacheType, expire, cacheNullValue, syncLocal, localExpire, null);}public K, V CacheK, V create(String area, String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal, Duration localExpire, Integer localLimit) {return create(area, name, cacheType, expire, cacheNullValue, syncLocal, localExpire, localLimit, false);}public K, V CacheK, V create(String area, String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal, Duration localExpire, Integer localLimit, Boolean useAreaInPrefix) {return create(area, name, cacheType, expire, cacheNullValue, syncLocal, localExpire, localLimit, useAreaInPrefix, false, null);}public K, V CacheK, V create(String area, String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal, Duration localExpire, Integer localLimit, Boolean useAreaInPrefix, Boolean penetrationProtect, Duration penetrationProtectTimeout) {QuickConfig.Builder builder StringUtils.isEmpty(area) ? QuickConfig.newBuilder(name) : QuickConfig.newBuilder(area, name);builder.cacheType(cacheType);builder.expire(expire);if (cacheType CacheType.BOTH) {builder.syncLocal(syncLocal);}builder.localExpire(localExpire);builder.localLimit(localLimit);builder.cacheNullValue(cacheNullValue);builder.useAreaInPrefix(useAreaInPrefix);if (ObjectUtils.isNotEmpty(penetrationProtect)) {builder.penetrationProtect(penetrationProtect);if (BooleanUtils.isTrue(penetrationProtect) ObjectUtils.isNotEmpty(penetrationProtectTimeout)) {builder.penetrationProtectTimeout(penetrationProtectTimeout);}}QuickConfig quickConfig builder.build();return create(quickConfig);}SuppressWarnings(unchecked)private K, V CacheK, V create(QuickConfig quickConfig) {return cacheManager.getOrCreateCache(quickConfig);} } 4.4.2.2 Spring Cache扩展实现类JetCacheSpringCache package cn.kuafu.cloud.base.cache.jetcache.enhance;import cn.hutool.crypto.SecureUtil; import cn.kuafu.cloud.base.assistant.core.json.jackson2.utils.JacksonUtils; import com.alicp.jetcache.Cache; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.lang.Nullable;import java.util.concurrent.Callable;/*** author ningzhaosheng* date 2022/8/23 16:54:36* description 基于 JetCache 的 Spring Cache 扩展*/ public class JetCacheSpringCache extends AbstractValueAdaptingCache {private static final Logger log LoggerFactory.getLogger(JetCacheSpringCache.class);private final String cacheName;private final CacheObject, Object cache;public JetCacheSpringCache(String cacheName, CacheObject, Object cache, boolean allowNullValues) {super(allowNullValues);this.cacheName cacheName;this.cache cache;}Overridepublic String getName() {return this.cacheName;}Overridepublic final CacheObject, Object getNativeCache() {return this.cache;}OverrideNullableprotected Object lookup(Object key) {Object value cache.get(key);if (ObjectUtils.isNotEmpty(value)) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - Lookup data in KuaFuCloud-Base-Cache cache, value is : [{}], JacksonUtils.toJson(value));return value;}return null;}SuppressWarnings(unchecked)OverrideNullablepublic T T get(Object key, CallableT valueLoader) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - Get data in KuaFuCloud-Base-Cache cache, key: {}, key);return (T) fromStoreValue(cache.computeIfAbsent(key, k - {try {return toStoreValue(valueLoader.call());} catch (Throwable ex) {throw new ValueRetrievalException(key, valueLoader, ex);}}));}OverrideNullablepublic void put(Object key, Nullable Object value) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - Put data in KuaFuCloud-Base-Cache cache, key: {}, key);cache.put(key, this.toStoreValue(value));}OverrideNullablepublic ValueWrapper putIfAbsent(Object key, Nullable Object value) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - PutIfPresent data in KuaFuCloud-Base-Cache cache, key: {}, key);Object existing cache.putIfAbsent(key, toStoreValue(value));return toValueWrapper(existing);}Overridepublic void evict(Object key) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - Evict data in KuaFuCloud-Base-Cache cache, key: {}, key);cache.remove(key);}Overridepublic boolean evictIfPresent(Object key) {log.trace([KuaFuCloud-Base-Cache] |- CACHE - EvictIfPresent data in KuaFuCloud-Base-Cache cache, key: {}, key);return cache.remove(key);}Overridepublic void clear() {log.trace([KuaFuCloud-Base-Cache] |- CACHE - Clear data in KuaFuCloud-Base-Cache cache.);cache.close();} }4.4.2.3 Spring CacheManager扩展实现类JetCacheSpringCacheManager package cn.kuafu.cloud.base.cache.jetcache.enhance;import cn.kuafu.cloud.base.assistant.core.definition.constants.SymbolConstants; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.lang.Nullable;import java.time.Duration; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap;/*** author ningzhaosheng* date 2022/8/23 17:01:32* description 基于 JetCache 的 Spring Cache Manager 扩展*/ public class JetCacheSpringCacheManager implements CacheManager {private static final Logger log LoggerFactory.getLogger(JetCacheSpringCacheManager.class);private boolean dynamic true;private boolean allowNullValues true;private final MapString, Cache cacheMap new ConcurrentHashMap(16);private final JetCacheCreateCacheFactory jetCacheCreateCacheFactory;public JetCacheSpringCacheManager(JetCacheCreateCacheFactory jetCacheCreateCacheFactory) {this.jetCacheCreateCacheFactory jetCacheCreateCacheFactory;}public JetCacheSpringCacheManager(JetCacheCreateCacheFactory jetCacheCreateCacheFactory, String... cacheNames) {this.jetCacheCreateCacheFactory jetCacheCreateCacheFactory;setCacheNames(Arrays.asList(cacheNames));}public void setAllowNullValues(boolean allowNullValues) {this.allowNullValues allowNullValues;}public boolean isAllowNullValues() {return allowNullValues;}private void setCacheNames(Nullable CollectionString cacheNames) {if (cacheNames ! null) {for (String name : cacheNames) {this.cacheMap.put(name, createJetCache(name));}this.dynamic false;} else {this.dynamic true;}}protected Cache createJetCache(String name) {com.alicp.jetcache.CacheObject, Object cache jetCacheCreateCacheFactory.create(name);log.debug([KuaFuCloud-Base-Cache] |- CACHE - KuaFuCloud cache [{}] is CREATED., name);return new JetCacheSpringCache(name, cache, allowNullValues);}protected Cache createJetCache(String name, Duration expire) {com.alicp.jetcache.CacheObject, Object cache jetCacheCreateCacheFactory.create(name, expire, allowNullValues, true);log.debug([KuaFuCloud-Base-Cache] |- CACHE - KuaFuCloud cache [{}] with expire is CREATED., name);return new JetCacheSpringCache(name, cache, allowNullValues);}private String availableCacheName(String name) {if (StringUtils.endsWith(name, SymbolConstants.COLON)) {return name;} else {return name SymbolConstants.COLON;}}OverrideNullablepublic Cache getCache(String name) {String usedName availableCacheName(name);return this.cacheMap.computeIfAbsent(usedName, cacheName -this.dynamic ? createJetCache(cacheName) : null);}Overridepublic CollectionString getCacheNames() {return Collections.unmodifiableSet(this.cacheMap.keySet());} }4.4.2.4 自定义缓存管理类KuaFuCloudCacheManager package cn.kuafu.cloud.base.cache.jetcache.enhance;import cn.kuafu.cloud.base.assistant.core.definition.constants.SymbolConstants; import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import cn.kuafu.cloud.base.cache.core.properties.Expire; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache;import java.util.Map;/*** author ningzhaosheng* date 2022/8/23 16:59:23* description 自定义 缓存管理器*/ public class KuaFuCloudCacheManager extends JetCacheSpringCacheManager {private static final Logger log LoggerFactory.getLogger(KuaFuCloudCacheManager.class);private final CacheProperties cacheProperties;public KuaFuCloudCacheManager(JetCacheCreateCacheFactory jetCacheCreateCacheFactory, CacheProperties cacheProperties) {super(jetCacheCreateCacheFactory);this.cacheProperties cacheProperties;this.setAllowNullValues(cacheProperties.getAllowNullValues());}public KuaFuCloudCacheManager(JetCacheCreateCacheFactory jetCacheCreateCacheFactory, CacheProperties cacheProperties, String... cacheNames) {super(jetCacheCreateCacheFactory, cacheNames);this.cacheProperties cacheProperties;}/*** 覆盖createJetCache 方法加入过期时间策略* param name* return*/Overrideprotected Cache createJetCache(String name) {MapString, Expire expires cacheProperties.getExpires();if (MapUtils.isNotEmpty(expires)) {String key StringUtils.replace(name, SymbolConstants.COLON, cacheProperties.getSeparator());if (expires.containsKey(key)) {Expire expire expires.get(key);log.debug([KuaFuCloud-Base-Cache] |- CACHE - Cache [{}] is set to use CUSTOM expire., name);return super.createJetCache(name, expire.getTtl());}}return super.createJetCache(name);} } 4.4.3 Stamp 缓存签章接口封装 4.4.3.1 StampManager 缓存签章接口 package cn.kuafu.cloud.base.cache.jetcache.stamp;import cn.kuafu.cloud.base.cache.core.exception.StampDeleteFailedException; import cn.kuafu.cloud.base.cache.core.exception.StampHasExpiredException; import cn.kuafu.cloud.base.cache.core.exception.StampMismatchException; import cn.kuafu.cloud.base.cache.core.exception.StampParameterIllegalException; import com.alicp.jetcache.AutoReleaseLock; import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.InitializingBean;import java.time.Duration; import java.util.concurrent.TimeUnit;/*** author ningzhaosheng* date 2022/5/13 10:14:28* description Stamp 服务接口* 此Stamp非OAuth2 Stamp。而是用于在特定条件下生成后在一定时间就会消除的标记性Stamp。* 例如幂等、短信验证码、Auth State等用时生成然后进行验证之后再删除的标记Stamp。* param K 签章缓存对应Key值的类型。* param V 签章缓存存储数据对应的具体存储值的类型*/ public interface StampManagerK,V extends InitializingBean {/*** 过期时间** return {link Duration}*/Duration getExpire();/*** 保存与Key对应的Stamp签章值** param key 存储Key* param value 与Key对应的Stamp* param expireAfterWrite 过期时间* param timeUnit 过期时间单位*/void put(K key, V value, long expireAfterWrite, TimeUnit timeUnit);/*** 保存与Key对应的Stamp签章值** param key 存储Key* param value 与Key对应的Stamp值* param expire 过期时间{link Duration}*/default void put(K key, V value, Duration expire) {put(key, value, expire.toMillis(), TimeUnit.MILLISECONDS);}/*** 保存与Key对应的Stamp签章值** param key 存储Key* param value 与Key对应的Stamp值*/default void put(K key, V value) {put(key, value, getExpire());}/*** 生成缓存值策略方法该方法负责生成具体存储的值。** param key 签章存储Key值* return {link String}*/V nextStamp(K key);/*** 创建具体的Stamp签章值并存储至本地缓存** param key 签章存储Key值* param expireAfterWrite 写入之后过期时间。注意该值每次写入都会覆盖。如果有一个时间周期内的反复存取操作需要手动计算时间差。* param timeUnit 时间单位* return 创建的签章值*/default V create(K key, long expireAfterWrite, TimeUnit timeUnit) {V value this.nextStamp(key);this.put(key, value, expireAfterWrite, timeUnit);return value;}/*** 创建具体的Stamp签章值并存储至本地缓存** param key 签章存储Key值* param expire 过期时间{link Duration}* return 创建的签章值*/default V create(K key, Duration expire) {return create(key, expire.toMillis(), TimeUnit.MILLISECONDS);}/*** 创建具体的Stamp签章值并存储至本地缓存** param key 与签章存储Key值* return 创建的签章值*/default V create(K key) {return create(key, getExpire());}/*** 校验Stamp值与本地存储的Stamp 是否匹配** param key 与Stamp对应的Key值* param value 外部传入的Stamp值* return ture 匹配false 不匹配* throws StampParameterIllegalException 传入Stamp错误* throws StampHasExpiredException 本地数据中没有Stamp或者Stamp已经过期。* throws StampMismatchException Stamp与本地存储值不匹配*/boolean check(K key, V value) throws StampParameterIllegalException, StampHasExpiredException, StampMismatchException;/*** 根据key读取Stamp** param key 存储数据Key值* return 存储的Stamp值*/V get(K key);/*** 删除与Key对应的Stamp** param key 存储数据Key值* throws StampDeleteFailedException Stamp删除错误*/void delete(K key) throws StampDeleteFailedException;default boolean containKey(K key) {V value get(key);return ObjectUtils.isNotEmpty(value);}/*** 锁定值* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。** param key 存储Key* param expire 过期时间* param timeUnit 过期时间单位* return {link AutoReleaseLock}* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/AutoReleaseLock lock(K key, long expire, TimeUnit timeUnit);/*** 锁定值* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。** param key 存储Key* param expire 过期时间{link Duration}* return {link AutoReleaseLock}* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/default AutoReleaseLock lock(K key, Duration expire) {return lock(key, expire.toMillis(), TimeUnit.MILLISECONDS);}/*** 锁定值* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。* *** param key 存储Key* return {link AutoReleaseLock}* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/default AutoReleaseLock lock(K key) {return lock(key, getExpire());}/*** 锁定并执行操作* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。** param key 存储Key* param expire 过期时间* param timeUnit 过期时间单位* param action 需要执行的操作 {link Runnable}* return 是否执行成功* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/boolean lockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action);/*** 锁定并执行操作* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。** param key 存储Key* param expire 过期时间{link Duration}* param action 需要执行的操作 {link Runnable}* return 是否执行成功* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/default boolean lockAndRun(K key, Duration expire, Runnable action) {return lockAndRun(key, expire.toMillis(), TimeUnit.MILLISECONDS, action);}/*** 锁定并执行操作* p* 非堵塞的尝试获取一个锁如果对应的key还没有锁返回一个AutoReleaseLock否则立即返回空。如果Cache实例是本地的它是一个本地锁在本JVM中有效如果是redis等远程缓存它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。** param key 存储Key* param action 需要执行的操作 {link Runnable}* return 是否执行成功* see a hrefhttps://github.com/alibaba/jetcache/wiki/CacheAPI_CNJetCache Wiki/a*/default boolean lockAndRun(K key, Runnable action) {return lockAndRun(key, getExpire(), action);} } 4.4.3.2 抽象Stamp签章管理类AbstractStampManager package cn.kuafu.cloud.base.cache.jetcache.stamp;import cn.kuafu.cloud.base.cache.core.exception.StampDeleteFailedException; import cn.kuafu.cloud.base.cache.core.exception.StampHasExpiredException; import cn.kuafu.cloud.base.cache.core.exception.StampMismatchException; import cn.kuafu.cloud.base.cache.core.exception.StampParameterIllegalException; import cn.kuafu.cloud.base.cache.jetcache.utils.JetCacheUtils; import com.alicp.jetcache.AutoReleaseLock; import com.alicp.jetcache.Cache; import com.alicp.jetcache.anno.CacheType; import org.apache.commons.lang3.ObjectUtils;import java.time.Duration; import java.util.concurrent.TimeUnit;/*** param K 签章缓存对应Key值的类型。* param V 签章缓存存储数据对应的具体存储值的类型* author ningzhaosheng* date 2022/5/13 10:16:52* description 抽象Stamp管理*/ public abstract class AbstractStampManagerK, V implements StampManagerK, V {private static final Duration DEFAULT_EXPIRE Duration.ofMinutes(30);private String cacheName;private CacheType cacheType;private Duration expire;private CacheK, V cache;public AbstractStampManager(String cacheName) {this(cacheName, CacheType.BOTH);}public AbstractStampManager(String cacheName, CacheType cacheType) {this(cacheName, cacheType, DEFAULT_EXPIRE);}public AbstractStampManager(String cacheName, CacheType cacheType, Duration expire) {this.cacheName cacheName;this.cacheType cacheType;this.expire expire;this.cache JetCacheUtils.create(this.cacheName, this.cacheType, this.expire);}/*** 指定数据存储缓存** return {link Cache}*/protected CacheK, V getCache() {return this.cache;}Overridepublic Duration getExpire() {return this.expire;}public void setExpire(Duration expire) {this.expire expire;}Overridepublic boolean check(K key, V value) {if (ObjectUtils.isEmpty(value)) {throw new StampParameterIllegalException(Parameter Stamp value is null);}V storedStamp this.get(key);if (ObjectUtils.isEmpty(storedStamp)) {throw new StampHasExpiredException(Stamp is invalid!);}if (ObjectUtils.notEqual(storedStamp, value)) {throw new StampMismatchException(Stamp is mismathch!);}return true;}Overridepublic V get(K key) {return this.getCache().get(key);}Overridepublic void delete(K key) throws StampDeleteFailedException {boolean result this.getCache().remove(key);if (!result) {throw new StampDeleteFailedException(Delete Stamp From Storage Failed);}}Overridepublic void put(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {this.getCache().put(key, value, expireAfterWrite, timeUnit);}Overridepublic AutoReleaseLock lock(K key, long expire, TimeUnit timeUnit) {return this.getCache().tryLock(key, expire, timeUnit);}Overridepublic boolean lockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action) {return this.getCache().tryLockAndRun(key, expire, timeUnit, action);} } 4.4.3.3 抽象缓存计数类AbstractCountStampManager package cn.kuafu.cloud.base.cache.jetcache.stamp;import cn.hutool.crypto.SecureUtil; import cn.kuafu.cloud.base.cache.core.exception.MaximumLimitExceededException; import com.alicp.jetcache.anno.CacheType; import org.apache.commons.lang3.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert;import java.time.Duration;/*** author ningzhaosheng* date 2022/8/22 18:09:50* description 计数类型的缓存* 这里的泛型使用了 Long 主要是为了兼顾存储 System.currentTimeMillis()。否则类型不一致还要建两个 Stamp*/ public abstract class AbstractCountStampManager extends AbstractStampManagerString, Long {private static final Logger log LoggerFactory.getLogger(AbstractCountStampManager.class);public AbstractCountStampManager(String cacheName) {super(cacheName);}public AbstractCountStampManager(String cacheName, CacheType cacheType) {super(cacheName, cacheType);}public AbstractCountStampManager(String cacheName, CacheType cacheType, Duration expire) {super(cacheName, cacheType, expire);}/*** 在缓存有效期内进行计数** param identity 缓存 Key 的区分标识* param maxTimes 允许的最大限制次数* return 当前错误次数* throws MaximumLimitExceededException 超出最大限制次数错误*/public int counting(String identity, int maxTimes) throws MaximumLimitExceededException {return counting(identity, maxTimes, null);}/*** 在缓存有效期内进行计数** param identity 缓存 Key 的区分标识* param maxTimes 允许的最大限制次数* param expire 过期时间* return 当前错误次数* throws MaximumLimitExceededException 超出最大限制次数错误*/public int counting(String identity, int maxTimes, Duration expire) throws MaximumLimitExceededException {return counting(identity, maxTimes, expire, false);}/*** 在缓存有效期内进行计数** param identity 缓存 Key 的区分标识* param maxTimes 允许的最大限制次数* param expire 过期时间* param function 用于在日志中区分是哪个功能在调用。* return 当前错误次数* throws MaximumLimitExceededException 超出最大限制次数错误*/public int counting(String identity, int maxTimes, Duration expire, String function) throws MaximumLimitExceededException {return counting(identity, maxTimes, expire, false, function);}/*** 在缓存有效期内进行计数** param identity 缓存 Key 的区分标识* param maxTimes 允许的最大限制次数* param expire 过期时间* param useMd5 是否用 MD5 对区分标识进行混淆加密* return 当前错误次数* throws MaximumLimitExceededException 超出最大限制次数错误*/public int counting(String identity, int maxTimes, Duration expire, boolean useMd5) throws MaximumLimitExceededException {return counting(identity, maxTimes, expire, useMd5, AbstractCountStampManager);}/*** 在缓存有效期内进行计数** param identity 缓存 Key 的区分标识* param maxTimes 允许的最大限制次数* param expire 过期时间* param useMd5 是否用 MD5 对区分标识进行混淆加密* param function 用于在日志中区分是哪个功能在调用。* return 当前错误次数* throws MaximumLimitExceededException 超出最大限制次数错误*/public int counting(String identity, int maxTimes, Duration expire, boolean useMd5, String function) throws MaximumLimitExceededException {Assert.notNull(identity, identity cannot be null);String key useMd5 ? SecureUtil.md5(identity) : identity;String expireKey key _expire;Long index get(key);if (ObjectUtils.isEmpty(index)) {index 0L;}if (index 0) {// 第一次读取剩余次数因为缓存中还没有值所以先创建缓存同时缓存中计数为1。if (ObjectUtils.isNotEmpty(expire) !expire.isZero()) {// 如果传入的 expire 不为零那么就用 expire 参数值create(key, expire);put(expireKey, System.currentTimeMillis(), expire);} else {// 如果没有传入 expire 值那么就默认使用 StampManager 自身配置的过期时间create(key);put(expireKey, System.currentTimeMillis());}} else {// 不管是注解上配置Duration值还是StampProperties中配置的Duration值是不会变的// 所以第一次存入expireKey对应的System.currentTimeMillis()时间后这个值也不应该变化。// 因此这里只更新访问次数的标记值Duration newDuration calculateRemainingTime(expire, expireKey, function);put(key, index 1L, newDuration);// times 计数相当于数组的索引是 从0~n所以需要if (index maxTimes - 1) {throw new MaximumLimitExceededException(Requests are too frequent. Please try again later!);}}int times new Long(index 1L).intValue();log.debug([KuaFuCloud-Base-Cache] |- {} has been recorded [{}] times., function, times);return times;}/*** 计算剩余过期时间* p* 每次create或者put缓存的过期时间都会被覆盖。注意Jetcache put 方法的参数名expireAfterWrite。* 因为Jetcache没有Redis的incr之类的方法那么每次放入Times值都会更新过期时间实际操作下来是变相的延长了过期时间。** param configuredDuration 注解上配置的、且可以正常解析的Duration值* param expireKey 时间标记存储Key值。* return 还剩余的过期时间 {link Duration}*/private Duration calculateRemainingTime(Duration configuredDuration, String expireKey, String function) {Long begin get(expireKey);Long current System.currentTimeMillis();long interval current - begin;log.debug([KuaFuCloud-Base-Cache] |- {} operation interval [{}] millis., function, interval);Duration duration;if (!configuredDuration.isZero()) {duration configuredDuration.minusMillis(interval);} else {duration getExpire().minusMillis(interval);}return duration;} } 4.4.4 JetCache直接使用封装 4.4.4.1 JetCache单例工具类JetCacheUtils package cn.kuafu.cloud.base.cache.jetcache.utils;import cn.kuafu.cloud.base.cache.jetcache.enhance.JetCacheCreateCacheFactory; import com.alicp.jetcache.Cache; import com.alicp.jetcache.anno.CacheType; import org.apache.commons.lang3.ObjectUtils;import java.time.Duration;/*** author ningzhaosheng* date 2022/8/25 18:17:59* description JetCache 单例工具类*/ public class JetCacheUtils {private static volatile JetCacheUtils instance;private JetCacheCreateCacheFactory jetCacheCreateCacheFactory;private JetCacheUtils() {}private void init(JetCacheCreateCacheFactory jetCacheCreateCacheFactory) {this.jetCacheCreateCacheFactory jetCacheCreateCacheFactory;}private JetCacheCreateCacheFactory getJetCacheCreateCacheFactory() {return jetCacheCreateCacheFactory;}public static JetCacheUtils getInstance() {if (ObjectUtils.isEmpty(instance)) {synchronized (JetCacheUtils.class) {if (ObjectUtils.isEmpty(instance)) {instance new JetCacheUtils();}}}return instance;}public static void setJetCacheCreateCacheFactory(JetCacheCreateCacheFactory jetCacheCreateCacheFactory) {getInstance().init(jetCacheCreateCacheFactory);}public static K, V CacheK, V create(String name, Duration expire) {return create(name, expire, true);}public static K, V CacheK, V create(String name, Duration expire, Boolean cacheNullValue) {return create(name, expire, cacheNullValue, null);}public static K, V CacheK, V create(String name, Duration expire, Boolean cacheNullValue, Boolean syncLocal) {return create(name, CacheType.BOTH, expire, cacheNullValue, syncLocal);}public static K, V CacheK, V create(String name, CacheType cacheType) {return create(name, cacheType, null);}public static K, V CacheK, V create(String name, CacheType cacheType, Duration expire) {return create(name, cacheType, expire, true);}public static K, V CacheK, V create(String name, CacheType cacheType, Duration expire, Boolean cacheNullValue) {return create(name, cacheType, expire, cacheNullValue, null);}public static K, V CacheK, V create(String name, CacheType cacheType, Duration expire, Boolean cacheNullValue, Boolean syncLocal) {return getInstance().getJetCacheCreateCacheFactory().create(name, cacheType, expire, cacheNullValue, syncLocal);} } 4.4.5 JetCache注解方式启动封装 4.4.5.1 JetCache加载配置类JetCacheConfiguration package cn.kuafu.cloud.base.cache.jetcache.configuration;import cn.kuafu.cloud.base.cache.caffeine.configuration.CaffeineConfiguration; import cn.kuafu.cloud.base.cache.core.properties.CacheProperties; import cn.kuafu.cloud.base.cache.jetcache.enhance.JetCacheCreateCacheFactory; import cn.kuafu.cloud.base.cache.jetcache.enhance.KuaFuCloudCacheManager; import cn.kuafu.cloud.base.cache.jetcache.utils.JetCacheUtils; import cn.kuafu.cloud.base.cache.redis.configuration.CacheRedisConfiguration; import com.alicp.jetcache.CacheManager; import com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary;import javax.annotation.PostConstruct;/*** author ningzhaosheng* date 2022/5/13 10:20:09* description 新增JetCache配置解决JetCache依赖循环问题*/ Configuration(proxyBeanMethods false) EnableConfigurationProperties(CacheProperties.class) Import({CaffeineConfiguration.class, CacheRedisConfiguration.class}) AutoConfigureAfter(JetCacheAutoConfiguration.class) public class JetCacheConfiguration {private static final Logger log LoggerFactory.getLogger(JetCacheConfiguration.class);PostConstructpublic void postConstruct() {log.debug([KuaFuCloud-Base-Cache] |- SDK [Cache JetCache] Auto Configure.);}BeanConditionalOnClass(CacheManager.class)public JetCacheCreateCacheFactory jetCacheCreateCacheFactory(CacheManager jcCacheManager) {JetCacheCreateCacheFactory jetCacheCreateCacheFactory new JetCacheCreateCacheFactory(jcCacheManager);JetCacheUtils.setJetCacheCreateCacheFactory(jetCacheCreateCacheFactory);log.trace([KuaFuCloud-Base-Cache] |- Bean [Jet Cache Create Cache Factory] Auto Configure.);return jetCacheCreateCacheFactory;}BeanPrimaryConditionalOnMissingBeanpublic KuaFuCloudCacheManager kuaFuCloudCacheManager(JetCacheCreateCacheFactory jetCacheCreateCacheFactory, CacheProperties cacheProperties) {KuaFuCloudCacheManager kuaFuCloudCacheManager new KuaFuCloudCacheManager(jetCacheCreateCacheFactory, cacheProperties);kuaFuCloudCacheManager.setAllowNullValues(cacheProperties.getAllowNullValues());log.trace([KuaFuCloud-Base-Cache] |- Bean [Jet Cache KuaFuCloud Cache Manager] Auto Configure.);return kuaFuCloudCacheManager;} } 4.4.5.2 JetCache手动启动注解 package cn.kuafu.cloud.base.cache.jetcache.annotation;import cn.kuafu.cloud.base.cache.jetcache.configuration.JetCacheConfiguration; import org.springframework.context.annotation.Import;import java.lang.annotation.*;/*** author ningzhaosheng* date 2022/5/13 10:42:52* description 手动开启JetCache注入*/ Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Import(JetCacheConfiguration.class) public interface EnableKuaFuCloudJetCache {} 4.4.6 Jpa Hibernate二级缓存扩展 4.4.6.1 DomainDataStorageAccess 接口实现 package cn.kuafu.cloud.base.data.jpa.hibernate.cache.spi;import cn.hutool.crypto.SecureUtil; import cn.kuafu.cloud.base.assistant.core.definition.constants.BaseConstants; import cn.kuafu.cloud.base.assistant.core.definition.constants.SymbolConstants; import cn.kuafu.cloud.base.assistant.core.context.TenantContextHolder; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.cache.spi.support.DomainDataStorageAccess; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache;/*** author ningzhaosheng* date 2022/5/14 18:28:20* description 自定义Hibernate二级缓存DomainDataStorageAccess*/ public class KuaFuCloudDomainDataStorageAccess implements DomainDataStorageAccess {private static final Logger log LoggerFactory.getLogger(KuaFuCloudDomainDataStorageAccess.class);private Cache cache;public KuaFuCloudDomainDataStorageAccess() {}public KuaFuCloudDomainDataStorageAccess(Cache cache) {this.cache cache;}private String secure(Object key) {String original String.valueOf(key);if (StringUtils.isNotBlank(original) StringUtils.startsWith(original, sql:)) {String recent SecureUtil.md5(original);log.trace([KuaFuCloud-Base-Data] |- SPI - Secure the sql type key [{}] to [{}], original, recent);return recent;}return original;}private String getTenantId() {String tenantId TenantContextHolder.getTenantId();String result StringUtils.isNotBlank(tenantId) ? tenantId : BaseConstants.DEFAULT_TENANT_ID;log.trace([KuaFuCloud-Base-Data] |- SPI - Tenant identifier for jpa second level cache is : [{}], result);return StringUtils.toRootLowerCase(result);}private String wrapper(Object key) {String original secure(key);String tenantId getTenantId();String result tenantId SymbolConstants.COLON original;log.trace([KuaFuCloud-Base-Data] |- SPI - Current cache key is : [{}], result);return result;}private Object get(Object key) {Cache.ValueWrapper value cache.get(key);if (ObjectUtils.isNotEmpty(value)) {return value.get();}return null;}Overridepublic boolean contains(Object key) {String wrapperKey wrapper(key);Object value this.get(wrapperKey);log.trace([KuaFuCloud-Base-Data] | - SPI check is key : [{}] exist., wrapperKey);return ObjectUtils.isNotEmpty(value);}Overridepublic Object getFromCache(Object key, SharedSessionContractImplementor session) {String wrapperKey wrapper(key);Object value this.get(wrapperKey);log.trace([KuaFuCloud-Base-Data] | - SPI get from cache key is : [{}], value is : [{}], wrapperKey, value);return value;}Overridepublic void putIntoCache(Object key, Object value, SharedSessionContractImplementor session) {String wrapperKey wrapper(key);log.trace([KuaFuCloud-Base-Data] | - SPI put into cache key is : [{}], value is : [{}], wrapperKey, value);cache.put(wrapperKey, value);}Overridepublic void removeFromCache(Object key, SharedSessionContractImplementor session) {String wrapperKey wrapper(key);log.trace([KuaFuCloud-Base-Data] | - SPI remove from cache key is : [{}], wrapperKey);cache.evict(wrapperKey);}Overridepublic void evictData(Object key) {String wrapperKey wrapper(key);log.trace([KuaFuCloud-Base-Data] | - SPI evict key : [{}] from cache., wrapperKey);cache.evict(wrapperKey);}Overridepublic void clearCache(SharedSessionContractImplementor session) {this.evictData();}Overridepublic void evictData() {log.trace([KuaFuCloud-Base-Data] | - SPI clear all cache data.);cache.clear();}Overridepublic void release() {log.trace([KuaFuCloud-Base-Data] | - SPI cache release.);cache.invalidate();} } 4.4.6.2 RegionFactoryTemplate 模板扩展 package cn.kuafu.cloud.base.data.jpa.hibernate.cache.spi;import cn.hutool.extra.spring.SpringUtil; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext; import org.hibernate.cache.cfg.spi.DomainDataRegionConfig; import org.hibernate.cache.spi.support.DomainDataStorageAccess; import org.hibernate.cache.spi.support.RegionFactoryTemplate; import org.hibernate.cache.spi.support.StorageAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.springframework.cache.CacheManager;import java.util.Map;/*** author ningzhaosheng* date 2022/5/14 18:30:35* description 自定义Hibernate二级缓存RegionFactory*/ public class KuaFuCloudRegionFactory extends RegionFactoryTemplate {private CacheManager cacheManager;Overrideprotected StorageAccess createQueryResultsRegionStorageAccess(String regionName, SessionFactoryImplementor sessionFactory) {return new KuaFuCloudDomainDataStorageAccess(cacheManager.getCache(regionName));}Overrideprotected StorageAccess createTimestampsRegionStorageAccess(String regionName, SessionFactoryImplementor sessionFactory) {return new KuaFuCloudDomainDataStorageAccess(cacheManager.getCache(regionName));}Overrideprotected DomainDataStorageAccess createDomainDataStorageAccess(DomainDataRegionConfig regionConfig, DomainDataRegionBuildingContext buildingContext) {return new KuaFuCloudDomainDataStorageAccess(cacheManager.getCache(regionConfig.getRegionName()));}Overrideprotected void prepareForUse(SessionFactoryOptions settings, Map configValues) {this.cacheManager SpringUtil.getBean(kuaFuCloudCacheManager);}Overrideprotected void releaseFromUse() {cacheManager null;} } 五、缓存 Start 启动组件封装 5.1 组件说明 借鉴SpringBoot Start 启动风格基于Spring SPI机制加载各配置类封装成cache-spring-boot-starter组件 5.2 组件结构图 5.3 组件Maven依赖 dependenciesdependencygroupId${project.groupId}/groupIdartifactIdcache-redisson-sdk/artifactId/dependencydependencygroupId${project.groupId}/groupIdartifactIdcache-jetcache-sdk/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-cache/artifactId/dependency/dependencies5.4 组件功能实现源码 5.4.1 组件启动类 package cn.kuafu.cloud.base.cache.starter.autoconfigure;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** author ningzhaosheng* date 2022/5/13 15:12:18* description Cache 配置*/ Configuration(proxyBeanMethods false) public class AutoConfiguration {private static final Logger log LoggerFactory.getLogger(AutoConfiguration.class);PostConstructpublic void postConstruct() {log.info([KuaFuCloud-Base-Cache] |- Starter [Base Cache Starter] Auto Configure.);} } 5.4.2 SPI加载机制配置文件 cn.kuafu.cloud.base.cache.starter.autoconfigure.AutoConfiguration cn.kuafu.cloud.base.cache.jetcache.configuration.JetCacheConfiguration JetCache缓存框架整合Caffeine、Redis实现一级缓存、二级缓存进而扩展JPA Hibernate、Mybatis两款ORM缓存框架的二级缓存扩展Spring Cache缓存实现Spring Session 共享等功能的缓存方案就实现完成了。如果对缓存扩展点和缓存原理不熟悉可以参考文章基于JetCache整合实现一级、二级缓存方案前置基础知识与原理-CSDN博客 好了本次分享就到这里如果帮助到大家欢迎大家点赞关注收藏有疑问也欢迎大家评论留言
http://www.zqtcl.cn/news/752209/

相关文章:

  • 外贸联系网站wordpress 优惠券 插件
  • 公司网站开发费用兴田德润官方网站深圳百度快照优化
  • 做网站需要备案么行业网站策划
  • 去年做啥网站能致富周口seo推广
  • 主体负责人电话修改 网站备案什么样算网站需要备案
  • 网站建站免费空间外贸网站建设与优化
  • 网站极简设计建立网站基本知识
  • 网站建设管理标准wordpress rss采集
  • 乐清网站建设费用装修房子的app软件哪个好
  • 专业网站搭建运营工业网站素材
  • 建网站要会什么wordpress电影下载站
  • 济南设计网站的公司西安模板网站建设
  • 网站搜索功能如何实现网络培训学习心得体会
  • 网站设计方案书ppt网站展示型推广
  • 中国建设注册管理中心网站首页大连地区建设网站
  • 广州致峰网站建设藁城网络推广
  • 怎么做免费个人网站wordpress dux 5.3
  • 手机触屏版网站网站功能介绍
  • 商场设计案例青岛百度快速排名优化
  • 制作网站要步骤湖北省建设厅网站上岗证查询
  • 网站建设制作公司都选万维科技制作网站需要注意什么
  • jsp小型网站开发wordpress微博插件
  • app充值网站开发怎么去做网站
  • 合肥建站网站模板word上下页纸张方向
  • 大学跳蚤市场网站建设哈尔滨网站建设
  • 网站开发合同中的知识产权条款怎么给公司建网站
  • 网站代维护wordpress 主题中心
  • 中铁广州建设有限公司网站临安做企业网站的公司
  • 国内可访问的海外网站和应用重庆好玩还是成都好玩
  • 定制开发小程序天津做网站优化的公司