<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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; } } }
快取元件 | 型別 |
---|---|
HAZELCAST | 分散式快取 |
INFINISPAN | 分散式快取 |
COUCHBASE | 分散式快取 |
REDIS | 分散式快取 |
CAFFEINE | 本地快取 |
CACHE2K | 本地快取 |
隨著硬體系統系統擴充套件和軟體升級,快取在應用中的地位和可應用性日漸提升,SpringBoot 為此設計了一套通用快取機制(規範)
此規範設計了兩個頂層介面 Cache 和 CacheManager 即快取和快取管理,通過實現CacheManager 引入快取元件,即可在SpringBoot專案內通過註解方便的設定快取
通過 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); }
註解 | 功能 |
---|---|
@EenableCacheing | 啟用註解式快取的功能,一般加在專案啟動類上 |
@Cacheable | 如果存在快取,則返回快取資訊;不存在則獲取值並新增到快取 |
@CachePut | 新增快取,可用於更新方法(強制將方法返回值新增到指定Key) |
@CacheEvict | 刪除快取 |
@Caching | 打包操作,將上面幾種註解打包在一起作用 |
@CacheConfig | 通用設定註解,如果要對某個物件設定快取,可以將此註解標註在類上設定快取名、主鍵生成器等 |
通過 SpringBoot 整合 Caffeine 進行快取註解演示,相關版本資訊參考依賴
<?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); } }
如果不通過Yml指定快取實現型別,則將使用預設實現
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; } }
定義一個 User 物件
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; }
定義一個 Controller 類用於測試
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; } }
啟動專案,新增強制快取
利用 Cacheable 嘗試重新整理快取(返回已存在值)
刪除快取
再次利用 Cacheable 嘗試重新整理快取(上面清除後則可重新整理)
自動過期測試,通過 CachePut 新增使用者資訊
嘗試用 Cacheable 重新整理快取,則 10S 後可生效
10 秒後
快取物件資訊
到此這篇關於SpringBoot 下整合快取工具類 CacheManager的文章就介紹到這了,更多相關Java快取工具類Cache內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45