userMap = new HashMap<>();
{
userMap.put("user01", new User("user01", "addr"));
userMap.put("user02", new User("user03", "addr"));
userMap.put("user03", new User("user03", "addr"));
}
/**
* 获取或加载缓存项
*
* 注:sync=false,CaffeineCache在定时刷新过期缓存时,是通过get(Object key)来获取缓存项,由于没有valueLoader(加载缓存项的具体逻辑),所以定时刷新缓存时,缓存项过期则会被淘汰。
*/
@Cacheable(value = "userCache", key = "#userId")
public User queryUser(String userId) {
User user = userMap.get(userId);
try {
Thread.sleep(1000);// 模拟加载数据的耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("加载数据:{}", user);
return user;
}
@Cacheable(value = "queryUserSync", key = "#userId", sync = true)
public User queryUserSync(String userId) {
User user = userMap.get(userId);
logger.info("加载数据:{}", user);
return user;
}
/**
* 获取或加载缓存项
*
* 注:因底层是基于caffeine来实现一级缓存,所以利用的caffeine本身的同步机制来实现
* sync=true 则表示并发场景下同步加载缓存项,
* sync=true,是通过get(Object key, Callable valueLoader)来获取或加载缓存项,此时valueLoader(加载缓存项的具体逻辑)会被缓存起来,所以CaffeineCache在定时刷新过期缓存时,缓存项过期则会重新加载。
* sync=false,是通过get(Object key)来获取缓存项,由于没有valueLoader(加载缓存项的具体逻辑),所以CaffeineCache在定时刷新过期缓存时,缓存项过期则会被淘汰。
*
* 建议:设置@Cacheable的sync=true,可以利用Caffeine的特性,防止缓存击穿(方式同一个key和不同key)
*/
@Cacheable(value = "queryUserSyncList", key = "#userId", sync = true)
public List queryUserSyncList(String userId) {
User user = userMap.get(userId);
List list = new ArrayList();
list.add(user);
logger.info("加载数据:{}", list);
return list;
}
/**
* 设置缓存
* 注:通过 @CachePut 标注的方法添加的缓存项,在CaffeineCache的定时刷新过期缓存任务执行时,缓存项过期则会被淘汰。
* 如果先执行了 @Cacheable(sync = true) 标注的方法,再执行 @CachePut 标注的方法,那么在CaffeineCache的定时刷新过期缓存任务执行时,缓存项过期则会重新加载。
*/
@CachePut(value = "userCacheSync", key = "#userId")
public User putUser(String userId, User user) {
return user;
}
/**
* 淘汰缓存
*/
@CacheEvict(value = "userCacheSync", key = "#userId")
public String evictUserSync(String userId) {
return userId;
}
}
```
### 手动构建Cache
详细的构建方法参见如下单元测试类:
```
com.coy.l2cache.test.GuavaCacheTest
com.coy.l2cache.test.CaffeineCacheTest
com.coy.l2cache.test.RedisCacheTest
com.coy.l2cache.test.CompositeCacheTest
com.coy.l2cache.test.KafkaCacheSyncPolicyTest
```
下面列举`CaffeineCacheTest`中的一部分使用场景:
```java
CacheConfig cacheConfig = new CacheConfig();
CaffeineCache cache;
Callable callable;
@before
public void before() {
// 默认配置 CAFFEINE
cacheConfig.setCacheType(CacheType.CAFFEINE.name())
.setAllowNullValues(true)
.getCaffeine()
.setDefaultSpec("initialCapacity=10,maximumSize=200,refreshAfterWrite=2s,recordStats")
.setAutoRefreshExpireCache(true)
.setRefreshPoolSize(3)
.setRefreshPeriod(5L);
cacheConfig.getCacheSyncPolicy()
.setType(CacheSyncPolicyType.REDIS.name());
// 构建缓存同步策略
CacheSyncPolicy cacheSyncPolicy = new RedisCacheSyncPolicy()
.setCacheConfig(cacheConfig)
.setCacheMessageListener(new CacheMessageListener(cacheConfig.getInstanceId()))
.setActualClient(Redisson.create());
cacheSyncPolicy.connnect();
// 构建cache
cache = (CaffeineCache) new CaffeineCacheBuilder()
.setCacheConfig(cacheConfig)
.setExpiredListener(new DefaultCacheExpiredListener())
.setCacheSyncPolicy(cacheSyncPolicy)
.build("localCache");
callable = new Callable() {
AtomicInteger count = new AtomicInteger(1);
@Override
public String call() throws Exception {
String result = "loader_value" + count.getAndAdd(1);
System.out.println("loader value from valueLoader, return " + result);
return result;
}
};
System.out.println("cacheName: " + cache.getCacheName());
System.out.println("level: " + cache.getCacheName());
System.out.println("actualCache: " + cache.getActualCache().getClass().getName());
System.out.println();
}
@Test
public void caffeineCacheTest() throws InterruptedException {
String key = "key1";
String value = "value1";
cache.put(key, value);// 设置缓存项
Object value1 = cache.get(key);// 获取缓存项
System.out.println(String.format("get key=%s, value=%s", key, value1));
String value = cache.get(key, callable);// 获取或设置缓存项
System.out.println(String.format("get key=%s, value=%s", key, value));
}
```