Spring 框架支援透明地為應用程式新增快取。其核心是將快取應用於方法,從而根據快取中可用的資訊減少程式Method的執行次數。快取應用,不會干擾呼叫者。只要使用@EnableCaching註解啟用快取支持,Spring Boot 就會自動配置快取基礎架構,當Method 被標註為 @Cacheable 後,Spring 會在第一次執行該Method時執行實際的邏輯,並將返回結果存入快取中。隨後的執行該Method將直接從快取中返回結果,而不會再次執行方法,這樣可以顯著提高應用程式的性能。配合多主機分散環境中執行,開發應用系統配置兩層快取,準備提供 Caffeine(JVM in-memory)與 Redis(分散式)快取的設定說明、使用範例、最佳實務,以及針對專案現有 util 的測試範例(CaffeineCacheUtilsTest、RedisCacheUtilsTest)。
Caffeine: 輕量、效能高的 JVM 內部快取,適合單一節點快取資料及加速讀取。專案位於 tw.lewishome.webapp.base.cache.caffeine,主要檔案:CaffeineConfiguration、CaffeineCacheUtils。
Redis: 分散式快取/資料存放,適用跨節點共用快取資料或需持久化的情境。專案位於 tw.lewishome.webapp.base.cache.redis,主要檔案:RedisConfiguration(Lettuce、RedisTemplate、redisCacheManager)。
原先將Caffeine 與 Redis一起介紹,但是發現存檔後,總是最後一段程式碼會被截斷,這裡只說明Caffeine快取暫存處理。
** Redis快取暫存處理,請參考 URL: https://ithelp.ithome.com.tw/articles/10398869 **
CaffeineConfiguration
位置:src/main/java/tw/lewishome/webapp/base/cache/caffeine/CaffeineConfiguration.java
caffeineConfig() → 回傳 com.github.benmanes.caffeine.cache.Caffeine 建構器。CACHE_TIME(分鐘),預設 3。@Primary caffeineCacheManager(CaffeineCacheManager)。addCatchValue、getCacheKeyValue、getAllCacheName、getAllCacheNameKeys、getCacheAllKeyValueMap、evictOneCaches、evictOneCachesKey、evictAllCaches。@Qualifier("caffeineCacheManager") 取得 CacheManager,並依賴該 manager 來操作 cache。@Primary 的 caffeineCacheManager(若未另外指定),當想使用 Redis 做快取時,可在 config 中調整或透過 cacheNames 與 CacheManager 的組合策略。-注意事項
Caffeine 為 JVM 內部快取,資料不會跨進程或跨主機共享;若部署多個實例需搭配分散式快取(如 Redis)同步或使用其他手段保持一致性。
CACHE_TIME 設定為分鐘級別;根據資料存取頻率與記憶體限制調整,避免大量長期快取導致 OOM。
recordStats() 會開啟統計,若需監控快取命中率可保留;若擔心效能,可將其移除。
建議對常用快取名稱統一命名及集中管理,以便在 CaffeineCacheManager 初始化時預先建立。
package tw.lewishome.webapp.base.cache.caffeine;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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 com.github.benmanes.caffeine.cache.Caffeine;
import tw.lewishome.webapp.base.utility.common.SystemEnvReader;
import tw.lewishome.webapp.base.utility.common.TypeConvert;
/**
* Caffeine 快取設定(CaffeineConfiguration)。
*
* 本類別負責設定與初始化應用程式中使用的 Caffeine 快取,並將
* CaffeineCacheManager 注入為 Spring 的
* {@link org.springframework.cache.CacheManager}。
*
*
* <h2>主要功能說明:</h2>
* <ul>
* <li><b>caffeineConfig()</b>:建立 Caffeine
* 快取建構器(Caffeine.newBuilder()),並設定快取過期策略與統計記錄。</li>
* <li><b>caffeineCacheManager(Caffeine)</b>:初始化
* {@link org.springframework.cache.caffeine.CaffeineCacheManager},並設定預先建立的快取名稱與所使用的
* Caffeine 設定。</li>
* </ul>
*
* <h2>使用情境:</h2>
* <ul>
* <li>應用程式需要在記憶體層級使用快取以提升讀取效能時,注入此類別提供的 CacheManager。</li>
* <li>當需要統一管理快取失效策略(如 write/ access 逾時)時,可透過此類別集中設定。</li>
* <li>可依需求擴充不同快取名稱或調整 CaffeineBuilder 的其他參數(如容量上限、weigher 等)。</li>
* </ul>
*
* @author Lewis
* @since 2024
*/
@SuppressWarnings("unchecked")
@EnableCaching
@Configuration
public class CaffeineConfiguration {
@Autowired
SystemEnvReader systemEnvReader;
/**
* Fix for javadoc warning :
* use of default constructor, which does not provide a comment
* Constructs a new CaffeineCacheConfiguration instance.
* This is the default constructor, implicitly provided by the compiler
* if no other constructors are defined.
*/
public CaffeineConfiguration() {
// Constructor body (can be empty)
}
/**
* 建立並回傳 Caffeine 建構器,用於指定快取的逾時策略與統計選項。
*
*範例設定:
* <ul>
* <li>寫入後過期:1 分鐘(expireAfterWrite)</li>
* <li>最後存取後過期:1 分鐘(expireAfterAccess)</li>
* <li>啟用統計紀錄(recordStats)以便監控命中率等指標</li>
* </ul>
*
*
* @return a {@link com.github.benmanes.caffeine.cache.Caffeine} instance
*/
@SuppressWarnings("rawtypes")
@Bean
public Caffeine caffeineConfig() {
String cacheTimeOut = systemEnvReader.getProperty("CACHE_TIME" , "3");
int exprireTime = TypeConvert.toInteger(cacheTimeOut);
return Caffeine.newBuilder()
.expireAfterWrite(exprireTime, TimeUnit.MINUTES) // 寫入後 3 分鐘過期
.expireAfterAccess(exprireTime, TimeUnit.MINUTES) // 最後訪問後 3 分鐘過期
.recordStats();
}
/**
* 建立並回傳 {@link org.springframework.cache.CacheManager},此處為
* CaffeineCacheManager。
*
* 方法會使用注入的 Caffeine 建構器設定 CacheManager,並預先建立常用的快取區名稱(例如
* "catchUserMenuItems"、"catchUserAuthSeq"),便於應用程式啟動後立即使用。
*
* @param caffeine 注入的 {@link com.github.benmanes.caffeine.cache.Caffeine} 建構器
* @return 已設定完成的 {@link org.springframework.cache.CacheManager}
*/
@Bean(name = "caffeineCacheManager")
@SuppressWarnings({ "rawtypes" })
@Primary
public CacheManager caffeineCacheManager(Caffeine caffeine) {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.getCache("catchUserMenuItems");
caffeineCacheManager.getCache("catchUserAuthSeq");
caffeineCacheManager.setCaffeine(caffeine);
return caffeineCacheManager;
}
}
CaffeineCacheUtils 工具方法摘要
位置:src/main/java/tw/lewishome/webapp/base/cache/caffeine/CaffeineCacheUtils.java
| 方法 | 參數 | 回傳值 | 說明 |
|---|---|---|---|
addCatchValue |
cacheName, cacheKey, cacheValue | Boolean | 新增快取值;若參數非法拋出 NullPointerException |
getCacheKeyValue |
cacheName, cacheKey | Object | 取得快取值;若找不到拋出 NullPointerException |
getAllCacheName |
無 | List | 取得所有 Cache 名稱列表 |
getAllCacheNameKeys |
cacheName | List | 取得指定 Cache 的所有 Key(CaffeineCache 內部實作遍歷 keySet) |
getCacheAllKeyValueMap |
cacheName | Map<String, Object> | 取得指定 Cache 的所有 Key/Value 對 |
evictOneCaches |
cacheName | void | 清空指定 Cache 的所有資料 |
evictOneCachesKey |
cacheName, cacheKey | void | 清除指定 Cache 的特定 Key |
evictAllCaches |
無 | void | 清空所有 Cache 資料 |
關鍵特性
getAllCacheNameKeys() 直接檢查 nativeCache instanceof CaffeineCache 並透過 Caffeine 的內部 Map 遍歷 keySet,效率高且可靠。package tw.lewishome.webapp.base.cache.caffeine;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;
/**
* Caffeine Cache 快取工具類別,提供管理與操作快取的功能。
*
*此類別提供以下主要功能:
* <ul>
* <li>新增和讀取快取資料</li>
* <li>取得所有快取名稱和鍵值</li>
* <li>清除特定或所有快取資料</li>
* </ul>
*
*使用方式:
* <pre>
* @Autowired
* private CaffeineCacheUtils cacheUtils;
*
* // 新增快取
* cacheUtils.addCatchValue("cacheName", "key", value);
*
* // 讀取快取
* Object value = cacheUtils.getCacheKeyValue("cacheName", "key");
* </pre>
*
* @author Lewis
* @version 1.0
* @since 1.0
*/
@Service
public class CaffeineCacheUtils {
/**
* Fix for javadoc warning :
* use of default constructor, which does not provide a comment
* Constructs a new CaffeineCacheUtils instance.
* This is the default constructor, implicitly provided by the compiler
* if no other constructors are defined.
*/
public CaffeineCacheUtils() {
// Constructor body (can be empty)
}
@Autowired
@Qualifier("caffeineCacheManager")
private CacheManager cacheManager;
/**
* 新增指定 CacheName 與 Key 的 Object 資料。
*
* @param cacheName Cache 名稱
* @param cacheKey Cache Key
* @param cacheValue Cache Value
* @return 新增成功回傳 true,失敗回傳 false
*/
public Boolean addCatchValue(String cacheName, String cacheKey, Object cacheValue) throws NullPointerException {
if (StringUtils.isBlank(cacheName)) {
throw new NullPointerException("cacheName is null or blank");
}
if (StringUtils.isBlank(cacheKey) || StringUtils.isBlank(cacheName)) {
throw new NullPointerException("Cache Key is null or blank");
}
Cache oneCache = cacheManager.getCache(cacheName);
if (oneCache == null) {
throw new NullPointerException("Cache Name not found / create");
}
oneCache.put(cacheKey, cacheValue);
return true;
}
/**
* 取得所有 Cache 名稱列表。
*
* @return 所有 Cache 名稱的 List
*/
public List<String> getAllCacheName() {
List<String> listAllCacheNames = new ArrayList<>(cacheManager.getCacheNames());
return listAllCacheNames;
}
/**
* 取得所有 Cache 名稱列表。
* @param cacheName Cache 名稱
* @return 所有 Cache 名稱的 List
*/
public List<String> getAllCacheNameKeys(String cacheName) {
List<String> listCacheKey = new ArrayList<>();
Cache oneCache = cacheManager.getCache(cacheName);
if (oneCache == null) {
return listCacheKey;
}
if (Boolean.TRUE.equals(oneCache instanceof CaffeineCache)) {
CaffeineCache caffeineCache = (CaffeineCache) oneCache;
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) caffeineCache.getNativeCache();
listCacheKey.addAll(nativeCache.asMap().keySet().stream().map(Object::toString).toList());
}
return listCacheKey;
}
/**
* 取得指定 CacheName 的所有 Key 與 Value 組成的 Map。
*
* @param cacheName Cache 名稱
* @return 所有 Key/Value 的 Map
*/
public Map<String, Object> getCacheAllKeyValueMap(String cacheName) {
Map<String, Object> mapAllCacheKeyValue = new HashMap<>();
if (cacheName == null) {
throw new NullPointerException("Cache is null");
}
List<String> listCacheKey = getAllCacheNameKeys(cacheName);
for (String oneKey : listCacheKey) {
Object oneValue = getCacheKeyValue(cacheName, oneKey);
mapAllCacheKeyValue.put(oneKey, oneValue);
}
return mapAllCacheKeyValue;
}
/**
* 取得指定 CacheName 與 Key 的資料 Object。
*
* @param cacheName Cache 名稱
* @param cacheKey Cache Key
* @return 查詢到的 Object,若無則回傳 null
*/
@SuppressWarnings("null")
public Object getCacheKeyValue(String cacheName, String cacheKey) throws NullPointerException {
if (cacheName == null) {
throw new NullPointerException("Cache is null");
}
Cache oneCache = cacheManager.getCache(cacheName);
if (oneCache == null) {
throw new NullPointerException("Cache Name not found");
}
if (cacheManager.getCache(cacheName).get(cacheKey) == null) {
throw new NullPointerException("Cache Key not found");
}
return cacheManager.getCache(cacheName).get(cacheKey).get();
}
/**
* 移除所有系統 Cache 資料。
*/
public void evictAllCaches() {
for (String oneCacheName : getAllCacheName()) {
if (oneCacheName == null) {
continue;
}
evictOneCaches(oneCacheName);
}
}
/**
* 移除指定 CacheName 的所有資料。 指定 CacheName 若不存在(Not null) 則增加一個內容為空的 CacheName。
*
* @param cacheName Cache 名稱
*/
public void evictOneCaches(String cacheName) {
if (cacheName == null) {
return;
}
Cache oneCache = cacheManager.getCache(cacheName);
if (oneCache == null || oneCache.getNativeCache() == null) {
return;
}
oneCache.clear();
}
/**
* 移除指定 CacheName 與 Key 的資料。
*
* @param cacheName Cache 名稱
* @param cacheKey Cache Key
*/
@SuppressWarnings("null")
public void evictOneCachesKey(String cacheName, String cacheKey) {
if (cacheName == null) {
return;
}
Cache oneCache = cacheManager.getCache(cacheName);
if (oneCache == null) {
return;
}
if (oneCache.get(cacheKey) == null) {
return;
}
if (oneCache.get(cacheKey).get() == null) {
return;
}
oneCache.evict(cacheKey);
}
}
CaffeineCacheUtilsTest
位置:src/test/java/tw/lewishome/webapp/base/cache/caffeine/CaffeineCacheUtilsTest.java
設定與初始化
@SpringBootTest 進行整合測試,載入完整應用程式上下文。@TestPropertySource(locations = "classpath:application.properties") 使用專案預設設定。@BeforeEach 初始化測試資料:在兩個 Cache(testCatch1、testCatch2)各放入兩筆 Key/Value。測試用例摘要
| 測試方法 | 目的 | 預期結果 |
|---|---|---|
testAddCatchValue_BlankKeyOrName |
驗證空白參數拒絕 | 拋出 NullPointerException(cacheName/cacheKey 為空白) |
testAddCatchValue_Success |
驗證新增成功 | 回傳 true;支援空字串與 null 值 |
testAddCatchValue_DuplicateSuccess |
驗證重複鍵行為 | 相同鍵覆寫不增加 key 計數 |
testGetAllCacheName |
驗證取得所有 Cache 名稱 | 包含設定檔預設 cache(catchUserMenuItems、catchUserAuthSeq)+ setUp 新增的 cache |
testGetAllCacheNameKeys |
驗證取得指定 Cache 的所有 key | 返回正確的 key 列表 |
testEvictAllCaches |
驗證清空所有 cache 資料 | Cache 名稱保留但內容清空 |
testEvictOneCaches_NullCacheName |
驗證 null cache 名稱處理 | 不拋出異常,忽略該操作 |
testEvictOneCaches_CacheNotFound |
驗證不存在 cache 的清除 | CacheManager 會新增該名稱但內容為空 |
testEvictOneCaches_CacheFound |
驗證清除指定 cache | 成功清空指定 cache 的所有資料 |
testEvictOneCachesKey_CacheNameNotFound |
驗證不存在 cache 的單鍵清除 | 不拋出異常 |
testEvictOneCachesKey_CacheKeyNotFound |
驗證不存在 key 的清除 | 不拋出異常 |
testGetCacheAllKeyValueMap |
驗證取得所有 Key/Value 對 | 返回正確的 Map 內容 |
testGetCatchValue_NullCacheName |
驗證 null cache 名稱查詢 | 拋出 NullPointerException |
testGetCatchValue_CacheNotFound |
驗證不存在 cache 查詢 | 拋出 NullPointerException |
testGetCatchValue_KeyNotFound |
驗證不存在 key 查詢 | 拋出 NullPointerException |
testGetCatchValue_KeyFound |
驗證成功取得快取值 | 返回正確的快取值 |
重點觀察
CaffeineCacheUtils 的參數嚴格性(null/空白拋出 NullPointerException)。package tw.lewishome.webapp.base.cache.caffeine;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
//啟動SpringBootTest
@SpringBootTest
// 指定適用Properties (這裡指定專案的properties 檔案,已可以另外指定 test專用的properties 檔案)
@TestPropertySource(locations = "classpath:application.properties")
class CaffeineCacheUtilsTest {
@Autowired
private CaffeineCacheUtils caffeineCacheUtils;
@BeforeEach
void setUp() {
caffeineCacheUtils.addCatchValue("testCatch1", "testCatchKey1", "testCatchValue1");
caffeineCacheUtils.addCatchValue("testCatch1", "testCatchKey2", "testCatchValue2");
caffeineCacheUtils.addCatchValue("testCatch2", "testCatchKey1", "testCatchValue3");
caffeineCacheUtils.addCatchValue("testCatch2", "testCatchKey2", "testCatchValue4");
}
@Test
void testAddCatchValue_BlankKeyOrName() {
NullPointerException ex1 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.addCatchValue("", "key", "value"));
assertNotNull(ex1.getMessage()); // assert NPE was thrown and contains a message
NullPointerException ex2 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.addCatchValue("cache1", "", "value"));
assertNotNull(ex2.getMessage()); // assert NPE was thrown and contains a message
NullPointerException ex3 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.addCatchValue(" ", "key", "value"));
assertNotNull(ex3.getMessage()); // assert NPE was thrown and contains a message
NullPointerException ex4 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.addCatchValue("cache1", " ", "value"));
assertNotNull(ex4.getMessage()); // assert NPE was thrown and contains a message
}
@Test
void testAddCatchValue_Success() {
Boolean result = caffeineCacheUtils.addCatchValue("cache1", "key", "value");
assertTrue(result);
Boolean result2 = caffeineCacheUtils.addCatchValue("cache1", "key2", "");
assertTrue(result2);
Boolean result3 = caffeineCacheUtils.addCatchValue("cache1", "key3", null);
assertTrue(result3);
}
@Test
void testAddCatchValue_DuplicateSuccess() {
List<String> listResult = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertEquals(2, listResult.size()); // 兩個初始值
Boolean result1 = caffeineCacheUtils.addCatchValue("testCatch1", "key", "value");
assertTrue(result1);
List<String> listResult1 = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertEquals(3, listResult1.size());
Boolean result2 = caffeineCacheUtils.addCatchValue("testCatch1", "key", "value");
assertTrue(result2);
List<String> listResult2 = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertEquals(3, listResult2.size()); // duplicate 不會增加
Boolean result3 = caffeineCacheUtils.addCatchValue("cache1", "key", "value");
assertTrue(result3);
List<String> listResult3 = caffeineCacheUtils.getAllCacheNameKeys("cache1");
assertEquals(1, listResult3.size()); // cache1 新增第一筆
}
@Test
void testGetAllCacheName() {
List<String> result = caffeineCacheUtils.getAllCacheName();
assertEquals(5, result.size()); // config file 兩個 + setUp 兩個
assertTrue(result.contains("testCatch1")); // from setUp
assertTrue(result.contains("testCatch2")); // from setUp
assertTrue(result.contains("catchUserMenuItems")); // from config
assertTrue(result.contains("catchUserAuthSeq")); // from config
}
@Test
void testGetAllCacheNameKeys() {
List<String> result = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
assertTrue(result.contains("testCatchKey1"));
assertTrue(result.contains("testCatchKey2"));
List<String> result1 = caffeineCacheUtils.getAllCacheNameKeys("catchUserAuthSeq");
assertEquals(0, result1.size()); // from config file has one default key
}
@Test
void testEvictAllCaches() {
caffeineCacheUtils.evictAllCaches();
List<String> result = caffeineCacheUtils.getAllCacheName();
// All caches should be evicted (cleaned) but cache names remain
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
List<String> listResult1 = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertEquals(0, listResult1.size());
List<String> listResult2 = caffeineCacheUtils.getAllCacheNameKeys("testCatch2");
assertEquals(0, listResult2.size());
List<String> listResult3 = caffeineCacheUtils.getAllCacheNameKeys("catchUserMenuItems");
assertEquals(0, listResult3.size());
List<String> listResult4 = caffeineCacheUtils.getAllCacheNameKeys("catchUserAuthSeq");
assertEquals(0, listResult4.size());
}
@Test
void testEvictOneCaches_NullCacheName() {
caffeineCacheUtils.evictOneCaches(null); // should be ignored (evictOneCaches with null)
List<String> result = caffeineCacheUtils.getAllCacheName();
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
}
@Test
void testEvictOneCaches_CacheNotFound() {
caffeineCacheUtils.evictOneCaches("cache1"); // cacheManager will add this cacheName but no data inside
List<String> result = caffeineCacheUtils.getAllCacheName();
assertEquals(5, result.size()); // setUp 兩個 + new one
// Should not throw
}
@Test
void testEvictOneCaches_CacheFound() {
List<String> listResult1 = caffeineCacheUtils.getAllCacheNameKeys("testCatch2");
assertEquals(2, listResult1.size()); // before evict
caffeineCacheUtils.evictOneCaches("testCatch2"); // caches should be evicted (cleaned) but cache names remain
List<String> result = caffeineCacheUtils.getAllCacheName();
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
List<String> listResult2 = caffeineCacheUtils.getAllCacheNameKeys("testCatch2");
assertEquals(0, listResult2.size()); // after evict
}
@Test
void testEvictOneCachesKey_CacheNameNotFound() {
caffeineCacheUtils.evictOneCachesKey("cache1", "testCatchKey3");
List<String> result = caffeineCacheUtils.getAllCacheName();
assertTrue(result.contains("cache1")); // cacheManager will add this cacheName but no data inside
}
@Test
void testEvictOneCachesKey_CacheKeyNotFound() { // cacheName found but key not found not throws exception NPE
List<String> listResult1 = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertTrue(listResult1.size()>0); // config file 兩個 + setUp 兩個
caffeineCacheUtils.evictOneCachesKey("testCatch1", "NoTestCatchKey1");
List<String> result = caffeineCacheUtils.getAllCacheName();
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
List<String> listResult2 = caffeineCacheUtils.getAllCacheNameKeys("testCatch1");
assertTrue(listResult2.size()>0); // config file 兩個 + setUp 兩個
}
@Test
void testGetCacheAllKeyValueMap() {
// Test
Map<String, Object> result = caffeineCacheUtils.getCacheAllKeyValueMap("testCatch1");
// Verify
assertNotNull(result);
assertTrue(result.size()>0); // config file 兩個 + setUp 兩個
assertEquals("testCatchValue1", (String) result.get("testCatchKey1"));
assertEquals("testCatchValue2", (String) result.get("testCatchKey2"));
}
@Test
void testGetCatchValue_NullCacheName() { // cacheName null already control in function return null
NullPointerException ex3 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.getCacheKeyValue(null, "testCatchKey1"));
assertNotNull(ex3.getMessage()); // assert NPE was thrown and contains a message
}
@Test
void testGetCatchValue_CacheNotFound() { // cacheName not exist already control in function return null
NullPointerException ex3 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.getCacheKeyValue("cache1", "testCatchKey1"));
assertNotNull(ex3.getMessage()); //
}
@Test
void testGetCatchValue_KeyNotFound() {
NullPointerException ex3 = assertThrows(NullPointerException.class,
() -> caffeineCacheUtils.getCacheKeyValue("testCatch1", "key1"));
assertNotNull(ex3.getMessage()); //
}
@Test
void testGetCatchValue_KeyFound() {
Object result = caffeineCacheUtils.getCacheKeyValue("testCatch1", "testCatchKey1");
assertEquals("testCatchValue1", result);
}
}