package com.demo.utils; import org.springframework.util.StringUtils; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * Description: 缓存工具类 * 1.部分方法未验证,如有问题请自行修改 * 2.其他方法请自行添加 * * @Author: zhx & moon hongxu_1234@163.com * @Date: 2022-04-07 20:54 * @version: V1.0.0 */ public class Cache { /** * 屏蔽工具类的无参构造 避免工具类被实例化 */ private Cache(){} /** * 缓存留存期 30min 1H 24H */ public static final long CACHE_HOLD_TIME_30M = 30 * 60 * 1000L; public static final long CACHE_HOLD_TIME_1H = 2 * CACHE_HOLD_TIME_30M; public static final long CACHE_HOLD_TIME_24H = 24 * CACHE_HOLD_TIME_1H; public static final long CACHE_HOLD_TIME_FOREVER = -1L; /** * 缓存容量、最少使用容量 */ private static final int CACHE_MAX_CAP = 1000; private static final int CLEAN_LRU_CAP = 800; /** * 缓存当前大小 */ private static AtomicInteger CACHE_CURRENT_SIZE = new AtomicInteger(0); /** * 缓存对象 */ private static final Map<String,Node> CACHE_MAP = new ConcurrentHashMap<>(CACHE_MAX_CAP); /** * 最少使用记录 */ private static final List<String> LRU_LIST = new LinkedList<>(); /** * 自动清理标志位 */ private static volatile boolean CLEAN_RUN_FLAG = false; /** * 默认30MIN * @param key * @param val */ public static void put(String key,Object val){ put(key,val,CACHE_HOLD_TIME_30M); } /** * 添加永久缓存 * @param key * @param val */ public static void putForever(String key,Object val){ put(key,val,CACHE_HOLD_TIME_FOREVER); } /** * 添加缓存 * @param key * @param val * @param ttlTime */ public static void put(String key,Object val,long ttlTime){ if (!StringUtils.hasLength(key) || null == val){ return; } checkSize(); updateCacheLru(key); CACHE_MAP.put(key,new Node(val,ttlTime)); } /** * 获取缓存信息 * @param key * @param clazz * @param <T> * @return */ public static <T> T get(String key,Class<T> clazz){ if (!StringUtils.hasLength(key) || !CACHE_MAP.containsKey(key)){ return null; } updateCacheLru(key); return (T) CACHE_MAP.get(key).getVal(); } /** * 更新最近使用位置 * @param key */ private static void updateCacheLru(String key){ synchronized (LRU_LIST){ LRU_LIST.remove(key); LRU_LIST.add(0,key); } } /** * 删除,成功则容量-1 * @param key */ private static boolean remove(String key){ Node node = CACHE_MAP.remove(key); if (null!=node){ CACHE_CURRENT_SIZE.getAndDecrement(); return true; } return false; } /** * 检查是否超过容量,先清理过期,在清理最少使用 */ private static void checkSize(){ if (CACHE_CURRENT_SIZE.intValue() > CACHE_MAX_CAP){ deleteTimeOut(); } if (CACHE_CURRENT_SIZE.intValue() > CLEAN_LRU_CAP){ deleteLru(); } } /** * 删除最久未使用,尾部删除 * 永久缓存不会被清除 */ private static void deleteLru(){ synchronized (LRU_LIST){ while (LRU_LIST.size() > CLEAN_LRU_CAP){ int lastIndex = LRU_LIST.size() - 1; String key = LRU_LIST.get(lastIndex); if (!CACHE_MAP.get(key).isForever() && remove(key)){ LRU_LIST.remove(lastIndex); } } } } /** * 删除过期 */ private static void deleteTimeOut(){ List<String> del = new LinkedList<>(); for (Map.Entry<String,Node> entry:CACHE_MAP.entrySet()){ if (entry.getValue().isExpired()){ del.add(entry.getKey()); } } for (String k:del){ remove(k); } } /** * 缓存是否已存在,过期则删除返回False * @param key * @return */ public static boolean contains(String key){ if (CACHE_MAP.containsKey(key)){ if (!CACHE_MAP.get(key).isExpired()){ return true; } if (remove(key)){ return false; } return true; } return false; } /** * 清空缓存 */ public static void clear(){ CACHE_MAP.clear(); CACHE_CURRENT_SIZE.set(0); LRU_LIST.clear(); } /** * 重置自动清理标志 * @param flag */ public static void setCleanRunFlag(boolean flag){ CLEAN_RUN_FLAG = flag; } /** * 自动清理过期缓存 */ private static void startAutoClean(){ if (!CLEAN_RUN_FLAG){ setCleanRunFlag(true); ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1); scheduledExecutor.scheduleAtFixedRate(()->{ try { Cache.setCleanRunFlag(true); while (CLEAN_RUN_FLAG){ Cache.deleteTimeOut(); } } catch (Exception e) { e.printStackTrace(); } },10,Cache.CACHE_HOLD_TIME_1H, TimeUnit.SECONDS); } } /** * 缓存对象类 */ public static class Node{ /** * 缓存值 */ private Object val; /** * 过期时间 */ private long ttlTime; public Node(Object val,long ttlTime){ this.val = val; if (ttlTime<0){ this.ttlTime = ttlTime; }else{ this.ttlTime = System.currentTimeMillis() + ttlTime; } } public Object getVal(){ return this.val; } public boolean isExpired(){ if (this.ttlTime<0){ return false; } return System.currentTimeMillis() > this.ttlTime; } public boolean isForever(){ if (this.ttlTime<0){ return true; } return false; } } }
# キャッシュ コンポーネント | タイプ |
---|---|
分散キャッシュ | |
分散キャッシュ | |
分散キャッシュ | |
分散キャッシュ | |
ローカル キャッシュ | |
ローカル キャッシュ |
この仕様では、2 つのトップレベルのインターフェイス キャッシュが設計されています。と CacheManager はキャッシュとキャッシュ管理です。CacheManager を実装し、キャッシュ コンポーネントを導入すると、SpringBoot プロジェクトのアノテーションを通じてキャッシュを簡単に設定できます。
//org.springframework.boot.autoconfigure.cache.CacheConfigurations static { Map<CacheType, String> mappings = new EnumMap<>(CacheType.class); mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName()); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName()); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName()); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName()); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName()); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName()); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName()); mappings.put(CacheType.CACHE2K, Cache2kCacheConfiguration.class.getName()); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName()); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName()); MAPPINGS = Collections.unmodifiableMap(mappings); }2. キャッシュ アノテーション
関数 | |
---|---|
注釈キャッシュを有効にする機能は、通常、プロジェクトの起動クラス | |
に追加されます。キャッシュがある場合はキャッシュ情報を返し、存在しない場合は値を取得してキャッシュに追加します | |
キャッシュを追加します。メソッドの更新に使用できます (メソッドの戻り値を指定された Key に強制的に追加します) | |
キャッシュの削除 | |
パッケージ操作、上記を組み合わせる 注釈は一緒にパッケージ化され、機能します | |
一般的な構成の注釈。オブジェクトのキャッシュを設定したい場合は、クラス上でこのアノテーションをマークしてキャッシュ名を設定できます。主キー ジェネレーターなど。 |
1.Pom 依存関係
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>LenovoTest</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>19</maven.compiler.source> <maven.compiler.target>19</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.0.0</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.version}</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.14.graal</version> </dependency> </dependencies> </project>
server: port: 8088 spring: cache: type: caffeine custom-caffeine: specs: ## 用户信息写入10S后过期 userInfo: maximumSize=10,expireAfterWrite=10s ## 登陆信息写入5S后过期 accessInfo: maximumSize=10,expireAfterWrite=5s
package com.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; /** * @author * @date * @since 1.8 */ @EnableCaching @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class,args); } }
package com.demo.comfig; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @author * @date * @since 1.8 */ @Configuration public class CustomCaffeineConfig { /** * 加载 Caffeine 配置 * @return */ @Bean(name = "caffeineProperties") @ConfigurationProperties(prefix = "custom-caffeine.specs") public Map<String,String> caffeineProperties(){ return new HashMap(16); } /** * 自定义 CacheManager * @param properties * @return */ @Bean @Primary public CacheManager caffeineManager(@Qualifier("caffeineProperties") Map<String,String> properties){ CaffeineCacheManager manager = new CaffeineCacheManager(); Map.Entry<String,String> entry; Iterator<Map.Entry<String,String>> iterator = properties.entrySet().iterator(); while (iterator.hasNext()){ entry = iterator.next(); manager.registerCustomCache(entry.getKey(), Caffeine.from(entry.getValue()).build()); } return manager; } }
5. テスト クラス
package com.demo.entity; import lombok.Data; /** * @author zhanghx19 * @date 2023-01-28 15:53 * @since 1.8 */ @Data public class UserInfo { private String name; private String account; private long age; }
テスト用のコントローラー クラスの定義
package com.demo.controller; import com.demo.entity.UserInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author * @date 2023-01-28 11:36 * @since 1.8 */ @RestController @RequestMapping("/test") public class TestController { /** * 注入缓存管理器,所有注解操作也可以直接操作管理器实现 */ @Autowired CacheManager cacheManager; /** * CachePut 强制刷新缓存 * @param id * @param val * @return */ @GetMapping("/update") @CachePut(cacheNames = "test" ,key = "#id") public String update(String id,String val){ //TODO Query Data By @{id} return val; } /** * Cacheable 查看缓存,存在则直接返回;否则查询数据,添加缓存并返回 * @param id * @param val * @return */ @GetMapping("/query") @Cacheable(cacheNames = "test" ,key = "#id" ) public String query(String id,String val){ //TODO Query Data By @{id} return val; } /** * 删除注解内指定的 缓存名下的 Key * @param id */ @GetMapping("/deleteTest") @CacheEvict(cacheNames = "test",key = "#id") public void deleteTest(String id){ } /** * 通过 cacheManager 删除缓存 * @param cacheNames * @param id */ @GetMapping("/deleteByNameAndKet") public void deleteByNameAndKet(String cacheNames,String id){ Cache cache = cacheManager.getCache(cacheNames); cache.evict(id); } /** * CachePut 强制缓存用户信息 且10秒后过期 * @param id * @param val * @return */ @GetMapping("/updateUser") @CachePut(cacheNames = "userInfo" ,key = "#id") public String updateUser(String id,String val){ return val; } /** * Cacheable 10秒内缓存不更新,丢失后可刷新为当前值 * @param id * @param val * @return */ @GetMapping("/queryUser") @Cacheable(cacheNames = "userInfo" ,key = "#id") public String queryUser(String id,String val){ return val; } /** * 缓存对象 * @param id * @param val * @return */ @GetMapping("/queryUserById") @Cacheable(cacheNames = "userInfo" ,key = "#id") public UserInfo getUserInfo(String id,String val){ UserInfo info = new UserInfo(); info.setAccount(id); info.setName(val); info.setAge(System.currentTimeMillis()); return info; } }
6. テスト レコード
Cacheable を使用してキャッシュを更新してみます (すでに存在する値を返します)
キャッシュを削除します
Cacheable を再度使用してキャッシュを更新してみます (上記でキャッシュをクリアした後に更新できます)
自動有効期限テスト、CachePut を介してユーザー情報を追加します
Cacheable を使用してキャッシュを更新してみてください。10 秒後に有効になります
10 秒後
キャッシュ オブジェクト情報
以上がSpringBoot で統合キャッシュ ツール クラス CacheManager を使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。