Spring Boot 緩存應(yīng)用實踐
點擊上方藍(lán)色“程序猿DD”,選擇“設(shè)為星標(biāo)”
回復(fù)“資源”獲取獨家整理的學(xué)習(xí)資料!

來源:cnblogs.com/jeffwongishandsome
緩存是最直接有效提升系統(tǒng)性能的手段之一。個人認(rèn)為用好用對緩存是優(yōu)秀程序員的必備基本素質(zhì)。本文結(jié)合實際開發(fā)經(jīng)驗,從簡單概念原理和代碼入手,一步一步搭建一個簡單的二級緩存系統(tǒng)。
一、通用緩存接口
1、緩存基礎(chǔ)算法
FIFO(First In First Out),先進先出,和OS里的FIFO思路相同,如果一個數(shù)據(jù)最先進入緩存中,當(dāng)緩存滿的時候,應(yīng)當(dāng)把最先進入緩存的數(shù)據(jù)給移除掉。 LFU(Least Frequently Used),最不經(jīng)常使用,如果一個數(shù)據(jù)在最近一段時間內(nèi)使用次數(shù)很少,那么在將來一段時間內(nèi)被使用的可能性也很小。 LRU(Least Recently Used),最近最少使用,如果一個數(shù)據(jù)在最近一段時間沒有被訪問到,那么在將來它被訪問的可能性也很小。也就是說,當(dāng)限定的空間已存滿數(shù)據(jù)時,應(yīng)當(dāng)把最久沒有被訪問到的數(shù)據(jù)移除。
2、接口定義
package com.power.demo.cache.contract;
import?java.util.function.Function;
/**
?* 緩存提供者接口
?**/
public?interface?CacheProviderService {
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????extends ?Object> T?get(String?key);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????**/
????extends?Object> T?get(String?key,?Function<String, T>?function);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????extends?Object> T?get(String?key,?Function<String, T>?function,?Long?expireTime);
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm,?Long?expireTime);
????/**
?????* 設(shè)置緩存鍵值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????**/
????extends?Object>?void?set(String?key, T obj);
????/**
?????* 設(shè)置緩存鍵值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????extends?Object>?void?set(String?key, T obj, Long expireTime);
????/**
?????* 移除緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????void?remove(String?key);
????/**
?????* 是否存在緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????boolean?contains(String?key);
}
二、本地緩存
1、什么是Guava
2、添加依賴
<dependency>
????<groupId>com.google.guavagroupId>
????<artifactId>guavaartifactId>
dependency>3、實現(xiàn)接口
/*
?* 本地緩存提供者服務(wù) (Guava Cache)
?* */
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
@Qualifier("localCacheService")
public?class?LocalCacheProviderImpl?implements?CacheProviderService {
????private?static?Map<String, Cache<String,?Object>> _cacheMap = Maps.newConcurrentMap();
????static?{
????????Cache<String,?Object> cacheContainer = CacheBuilder.newBuilder()
????????????????.maximumSize(AppConst.CACHE_MAXIMUM_SIZE)
????????????????.expireAfterWrite(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS)//最后一次寫入后的一段時間移出
????????????????//.expireAfterAccess(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS) //最后一次訪問后的一段時間移出
????????????????.recordStats()//開啟統(tǒng)計功能
????????????????.build();
????????_cacheMap.put(String.valueOf(AppConst.CACHE_MINUTE), cacheContainer);
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?extends ?Object> T?get(String?key) {
????????T obj =?get(key,?null,?null, AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function)?{
????????T obj =?get(key,?function,?key,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm)?{
????????T obj =?get(key,?function,?funcParm,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function,?Long?expireTime)?{
????????T obj =?get(key,?function,?key,?expireTime);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm,?Long?expireTime)?{
????????T obj =?null;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?obj;
????????}
????????expireTime = getExpireTime(expireTime);
????????Cache<String,?Object> cacheContainer = getCacheContainer(expireTime);
????????try?{
????????????if?(function?==?null)?{
????????????????obj = (T) cacheContainer.getIfPresent(key);
????????????}?else?{
????????????????final Long cachedTime = expireTime;
????????????????obj = (T) cacheContainer.get(key, () -> {
????????????????????T retObj =?function.apply(funcParm);
????????????????????return?retObj;
????????????????});
????????????}
????????}?catch?(Exception e) {
????????????e.printStackTrace();
????????}
????????return?obj;
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入值,這會直接覆蓋掉給定鍵之前映射的值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj) {
????????set(key, obj, AppConst.CACHE_MINUTE);
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入值,這會直接覆蓋掉給定鍵之前映射的值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj, Long expireTime) {
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return;
????????}
????????if?(obj ==?null) {
????????????return;
????????}
????????expireTime = getExpireTime(expireTime);
????????Cache<String,?Object> cacheContainer = getCacheContainer(expireTime);
????????cacheContainer.put(key, obj);
????}
????/**
?????* 移除緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?void?remove(String?key) {
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return;
????????}
????????long expireTime = getExpireTime(AppConst.CACHE_MINUTE);
????????Cache<String,?Object> cacheContainer = getCacheContainer(expireTime);
????????cacheContainer.invalidate(key);
????}
????/**
?????* 是否存在緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?boolean?contains(String?key) {
????????boolean?exists =?false;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?exists;
????????}
????????Object?obj =?get(key);
????????if?(obj !=?null) {
????????????exists =?true;
????????}
????????return?exists;
????}
????private?static?Lock lock =?new?ReentrantLock();
????private?Cache<String,?Object> getCacheContainer(Long expireTime) {
????????Cache<String,?Object> cacheContainer =?null;
????????if?(expireTime ==?null) {
????????????return?cacheContainer;
????????}
????????String?mapKey =?String.valueOf(expireTime);
????????if?(_cacheMap.containsKey(mapKey) ==?true) {
????????????cacheContainer = _cacheMap.get(mapKey);
????????????return?cacheContainer;
????????}
????????try?{
????????????lock.lock();
????????????cacheContainer = CacheBuilder.newBuilder()
????????????????????.maximumSize(AppConst.CACHE_MAXIMUM_SIZE)
????????????????????.expireAfterWrite(expireTime, TimeUnit.MILLISECONDS)//最后一次寫入后的一段時間移出
????????????????????//.expireAfterAccess(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS) //最后一次訪問后的一段時間移出
????????????????????.recordStats()//開啟統(tǒng)計功能
????????????????????.build();
????????????_cacheMap.put(mapKey, cacheContainer);
????????}?finally?{
????????????lock.unlock();
????????}
????????return?cacheContainer;
????}
????/**
?????* 獲取過期時間 單位:毫秒
?????*
?????* @param expireTime 傳人的過期時間 單位毫秒 如小于1分鐘,默認(rèn)為10分鐘
?????**/
????private?Long getExpireTime(Long expireTime) {
????????Long result = expireTime;
????????if?(expireTime ==?null?|| expireTime < AppConst.CACHE_MINUTE /?10) {
????????????result = AppConst.CACHE_MINUTE;
????????}
????????return?result;
????}
} 4、注意事項
expireAfterWrite:最后一次寫入后的一段時間移出。 expireAfterAccess:最后一次訪問后的一段時間移出。
三、分布式緩存
1、什么是Redis
Values:Strings、Hash、Lists、 Sets、 Sorted sets。考慮到Redis單線程操作模式,Value的粒度不應(yīng)該過大,緩存的值越大,越容易造成阻塞和排隊。
2、添加依賴
<dependency>
?????<groupId>org.springframework.bootgroupId>
?????<artifactId>spring-boot-starter-data-redisartifactId>
dependency>3、配置Redis
## Redis緩存相關(guān)配置
#Redis數(shù)據(jù)庫索引(默認(rèn)為0)
spring.redis.database=0
#Redis服務(wù)器地址
spring.redis.host=127.0.0.1
#Redis服務(wù)器端口
spring.redis.port=6379
#Redis服務(wù)器密碼(默認(rèn)為空)
spring.redis.password=123321
#Redis連接超時時間 默認(rèn):5分鐘(單位:毫秒)
spring.redis.timeout=300000ms
#Redis連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
spring.redis.jedis.pool.max-active=512
#Redis連接池中的最小空閑連接
spring.redis.jedis.pool.min-idle=0
#Redis連接池中的最大空閑連接
spring.redis.jedis.pool.max-idle=8
#Redis連接池最大阻塞等待時間(使用負(fù)值表示沒有限制)
spring.redis.jedis.pool.max-wait=-1ms
redisproperties/**
?* Redis緩存配置類
?*/
@Configuration
@EnableCaching
public?class?RedisConfig?extends?CachingConfigurerSupport {
????@Bean
????public?CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
????????return?RedisCacheManager.create(connectionFactory);
????}
????@Bean
????public?RedisTemplate<String,?Object> redisTemplate(RedisConnectionFactory factory) {
????????RedisTemplate<String,?Object> template =?new?RedisTemplate<>();
????????//Jedis的Key和Value的序列化器默認(rèn)值是JdkSerializationRedisSerializer
????????//經(jīng)實驗,JdkSerializationRedisSerializer通過RedisDesktopManager看到的鍵值對不能正常解析
????????//設(shè)置key的序列化器
????????template.setKeySerializer(new?StringRedisSerializer());
????????////設(shè)置value的序列化器 默認(rèn)值是JdkSerializationRedisSerializer
????????//使用Jackson序列化器的問題是,復(fù)雜對象可能序列化失敗,比如JodaTime的DateTime類型
????????// //使用Jackson2,將對象序列化為JSON
????????// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
????????// //json轉(zhuǎn)對象類,不設(shè)置默認(rèn)的會將json轉(zhuǎn)成hashmap
????????// ObjectMapper om = new ObjectMapper();
????????// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
????????// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
????????// jackson2JsonRedisSerializer.setObjectMapper(om);
????????// template.setValueSerializer(jackson2JsonRedisSerializer);
????????//將redis連接工廠設(shè)置到模板類中
????????template.setConnectionFactory(factory);
????????return?template;
????}
// //自定義緩存key生成策略
// @Bean
// public KeyGenerator keyGenerator() {
// return new KeyGenerator() {
// @Override
// public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
// StringBuffer sb = new StringBuffer();
// sb.append(target.getClass().getName());
// sb.append(method.getName());
// for (Object obj : params) {
// if (obj == null) {
// continue;
// }
// sb.append(obj.toString());
// }
// return sb.toString();
// }
// };
// }
}JdkSerializationRedisSerializer:使用Java序列化; StringRedisSerializer:序列化String類型的key和value; GenericToStringSerializer:使用Spring轉(zhuǎn)換服務(wù)進行序列化; JacksonJsonRedisSerializer:使用Jackson 1,將對象序列化為JSON; Jackson2JsonRedisSerializer:使用Jackson 2,將對象序列化為JSON; OxmSerializer:使用Spring O/X映射的編排器和解排器(marshaler和unmarshaler)實現(xiàn)序列化,用于XML序列化;

4、實現(xiàn)接口
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
@Qualifier("redisCacheService")
public?class?RedisCacheProviderImpl?implements?CacheProviderService {
????@Resource
????private?RedisTemplateObject > redisTemplate;
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?extends?Object> T?get(String?key) {
????????T obj =?get(key,?null,?null, AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function)?{
????????T obj =?get(key,?function,?key,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm)?{
????????T obj =?get(key,?function,?funcParm,?AppConst.CACHE_MINUTE);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function,?Long?expireTime)?{
????????T obj =?get(key,?function,?key,?expireTime);
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm,?Long?expireTime)?{
????????T obj =?null;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?obj;
????????}
????????expireTime = getExpireTime(expireTime);
????????try?{
????????????ValueOperationsObject> operations = redisTemplate.opsForValue();
????????????obj = (T) operations.get(key);
????????????if?(function?!=?null?&&?obj?==?null)?{
????????????????obj =?function.apply(funcParm);
????????????????if?(obj !=?null) {
????????????????????set(key, obj, expireTime);//設(shè)置緩存信息
????????????????}
????????????}
????????}?catch?(Exception e) {
????????????e.printStackTrace();
????????}
????????return?obj;
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入值,這會直接覆蓋掉給定鍵之前映射的值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj) {
????????set(key, obj, AppConst.CACHE_MINUTE);
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入值,這會直接覆蓋掉給定鍵之前映射的值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj, Long expireTime) {
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return;
????????}
????????if?(obj ==?null) {
????????????return;
????????}
????????expireTime = getExpireTime(expireTime);
????????ValueOperationsObject> operations = redisTemplate.opsForValue();
????????operations.set(key, obj);
????????redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
????}
????/**
?????* 移除緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?void?remove(String?key) {
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return;
????????}
????????redisTemplate.delete(key);
????}
????/**
?????* 是否存在緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?boolean?contains(String?key) {
????????boolean?exists =?false;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?exists;
????????}
????????Object?obj =?get(key);
????????if?(obj !=?null) {
????????????exists =?true;
????????}
????????return?exists;
????}
????/**
?????* 獲取過期時間 單位:毫秒
?????*
?????* @param expireTime 傳人的過期時間 單位毫秒 如小于1分鐘,默認(rèn)為10分鐘
?????**/
????private?Long getExpireTime(Long expireTime) {
????????Long result = expireTime;
????????if?(expireTime ==?null?|| expireTime < AppConst.CACHE_MINUTE /?10) {
????????????result = AppConst.CACHE_MINUTE;
????????}
????????return?result;
????}
}
四、緩存“及時”過期問題
五、二級緩存
##是否啟用本地緩存
spring.power.isuselocalcache=1
##是否啟用Redis緩存
spring.power.isuserediscache=1/*
?* 支持多緩存提供程序多級緩存的緩存幫助類
?* */
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
public?class?PowerCacheBuilder {
????@Autowired
????@Qualifier("localCacheService")
????private?CacheProviderService localCacheService;
????@Autowired
????@Qualifier("redisCacheService")
????private?CacheProviderService redisCacheService;
????private?static?List _listCacheProvider = Lists.newArrayList();
????private?static?final Lock providerLock =?new?ReentrantLock();
????/**
?????* 初始化緩存提供者 默認(rèn)優(yōu)先級:先本地緩存,后分布式緩存
?????**/
????private?List getCacheProviders() {
????????if?(_listCacheProvider.size() >?0) {
????????????return?_listCacheProvider;
????????}
????????//線程安全
????????try?{
????????????providerLock.tryLock(1000, TimeUnit.MILLISECONDS);
????????????if?(_listCacheProvider.size() >?0) {
????????????????return?_listCacheProvider;
????????????}
????????????String?isUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_LOCAL_CACHE);
????????????CacheProviderService cacheProviderService =?null;
????????????//啟用本地緩存
????????????if?("1".equalsIgnoreCase(isUseCache)) {
????????????????_listCacheProvider.add(localCacheService);
????????????}
????????????isUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_REDIS_CACHE);
????????????//啟用Redis緩存
????????????if?("1".equalsIgnoreCase(isUseCache)) {
????????????????_listCacheProvider.add(redisCacheService);
????????????????resetCacheVersion();//設(shè)置分布式緩存版本號
????????????}
????????????PowerLogger.info("初始化緩存提供者成功,共有"?+ _listCacheProvider.size() +?"個");
????????}?catch?(Exception e) {
????????????e.printStackTrace();
????????????_listCacheProvider = Lists.newArrayList();
????????????PowerLogger.error("初始化緩存提供者發(fā)生異常:{}", e);
????????}?finally?{
????????????providerLock.unlock();
????????}
????????return?_listCacheProvider;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?extends ?Object> T?get(String?key) {
????????T obj =?null;
????????//key = generateVerKey(key);//構(gòu)造帶版本的緩存鍵
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????obj = provider.get(key);
????????????if?(obj !=?null) {
????????????????return?obj;
????????????}
????????}
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function)?{
????????T obj =?null;
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????if?(obj ==?null) {
????????????????obj = provider.get(key,?function);
????????????}?else?if?(function?!=?null?&&?obj?!=?null)?{//查詢并設(shè)置其他緩存提供者程序緩存
????????????????provider.get(key,?function);
????????????}
????????????//如果callable函數(shù)為空 而緩存對象不為空 及時跳出循環(huán)并返回
????????????if?(function?==?null?&&?obj?!=?null)?{
????????????????return?obj;
????????????}
????????}
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm)?{
????????T obj =?null;
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????if?(obj ==?null) {
????????????????obj = provider.get(key,?function,?funcParm);
????????????}?else?if?(function?!=?null?&&?obj?!=?null)?{//查詢并設(shè)置其他緩存提供者程序緩存
????????????????provider.get(key,?function,?funcParm);
????????????}
????????????//如果callable函數(shù)為空 而緩存對象不為空 及時跳出循環(huán)并返回
????????????if?(function?==?null?&&?obj?!=?null)?{
????????????????return?obj;
????????????}
????????}
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object> T?get(String?key,?Function<String, T>?function,?long?expireTime)?{
????????T obj =?null;
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????if?(obj ==?null) {
????????????????obj = provider.get(key,?function,?expireTime);
????????????}?else?if?(function?!=?null?&&?obj?!=?null)?{//查詢并設(shè)置其他緩存提供者程序緩存
????????????????provider.get(key,?function,?expireTime);
????????????}
????????????//如果callable函數(shù)為空 而緩存對象不為空 及時跳出循環(huán)并返回
????????????if?(function?==?null?&&?obj?!=?null)?{
????????????????return?obj;
????????????}
????????}
????????return?obj;
????}
????/**
?????* 查詢緩存
?????*
?????* @param key 緩存鍵 不可為空
?????* @param function 如沒有緩存,調(diào)用該callable函數(shù)返回對象 可為空
?????* @param funcParm function函數(shù)的調(diào)用參數(shù)
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object, M?extends?Object> T?get(String?key,?Function?function,?M?funcParm,?long?expireTime)?{
????????T obj =?null;
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????if?(obj ==?null) {
????????????????obj = provider.get(key,?function,?funcParm,?expireTime);
????????????}?else?if?(function?!=?null?&&?obj?!=?null)?{//查詢并設(shè)置其他緩存提供者程序緩存
????????????????provider.get(key,?function,?funcParm,?expireTime);
????????????}
????????????//如果callable函數(shù)為空 而緩存對象不為空 及時跳出循環(huán)并返回
????????????if?(function?==?null?&&?obj?!=?null)?{
????????????????return?obj;
????????????}
????????}
????????return?obj;
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入或覆蓋值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj) {
????????//key = generateVerKey(key);//構(gòu)造帶版本的緩存鍵
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????provider.set(key, obj);
????????}
????}
????/**
?????* 設(shè)置緩存鍵值 直接向緩存中插入或覆蓋值
?????*
?????* @param key 緩存鍵 不可為空
?????* @param obj 緩存值 不可為空
?????* @param expireTime 過期時間(單位:毫秒) 可為空
?????**/
????public?extends?Object>?void?set(String?key, T obj, Long expireTime) {
????????//key = generateVerKey(key);//構(gòu)造帶版本的緩存鍵
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????provider.set(key, obj, expireTime);
????????}
????}
????/**
?????* 移除緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?void?remove(String?key) {
????????//key = generateVerKey(key);//構(gòu)造帶版本的緩存鍵
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return;
????????}
????????for?(CacheProviderService provider : getCacheProviders()) {
????????????provider.remove(key);
????????}
????}
????/**
?????* 是否存在緩存
?????*
?????* @param key 緩存鍵 不可為空
?????**/
????public?boolean?contains(String?key) {
????????boolean?exists =?false;
????????//key = generateVerKey(key);//構(gòu)造帶版本的緩存鍵
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?exists;
????????}
????????Object?obj =?get(key);
????????if?(obj !=?null) {
????????????exists =?true;
????????}
????????return?exists;
????}
????/**
?????* 獲取分布式緩存版本號
?????**/
????public?String?getCacheVersion() {
????????String?version =?"";
????????boolean?isUseCache = checkUseRedisCache();
????????//未啟用Redis緩存
????????if?(isUseCache ==?false) {
????????????return?version;
????????}
????????version = redisCacheService.get(AppConst.CACHE_VERSION_KEY);
????????return?version;
????}
????/**
?????* 重置分布式緩存版本 如果啟用分布式緩存,設(shè)置緩存版本
?????**/
????public?String?resetCacheVersion() {
????????String?version =?"";
????????boolean?isUseCache = checkUseRedisCache();
????????//未啟用Redis緩存
????????if?(isUseCache ==?false) {
????????????return?version;
????????}
????????//設(shè)置緩存版本
????????version =?String.valueOf(Math.abs(UUID.randomUUID().hashCode()));
????????redisCacheService.set(AppConst.CACHE_VERSION_KEY, version);
????????return?version;
????}
????/**
?????* 如果啟用分布式緩存,獲取緩存版本,重置查詢的緩存key,可以實現(xiàn)相對實時的緩存過期控制
?????*
?????* 如沒有啟用分布式緩存,緩存key不做修改,直接返回
?????**/
????public?String?generateVerKey(String?key) {
????????String?result = key;
????????if?(StringUtils.isEmpty(key) ==?true) {
????????????return?result;
????????}
????????boolean?isUseCache = checkUseRedisCache();
????????//沒有啟用分布式緩存,緩存key不做修改,直接返回
????????if?(isUseCache ==?false) {
????????????return?result;
????????}
????????String?version = redisCacheService.get(AppConst.CACHE_VERSION_KEY);
????????if?(StringUtils.isEmpty(version) ==?true) {
????????????return?result;
????????}
????????result =?String.format("%s_%s", result, version);
????????return?result;
????}
????/**
?????* 驗證是否啟用分布式緩存
?????**/
????private?boolean?checkUseRedisCache() {
????????boolean?isUseCache =?false;
????????String?strIsUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_REDIS_CACHE);
????????isUseCache =?"1".equalsIgnoreCase(strIsUseCache);
????????return?isUseCache;
????}
} @Test
public?void?testCacheVerson() throws Exception?{
????????String version = cacheBuilder.getCacheVersion();
????????System.out.println(String.format("當(dāng)前緩存版本:%s", version));
????????String cacheKey = cacheBuilder.generateVerKey("goods778899");
????????GoodsVO goodsVO =?new?GoodsVO();
????????goodsVO.setGoodsId(UUID.randomUUID().toString());
????????goodsVO.setCreateTime(new?Date());
????????goodsVO.setCreateDate(new?DateTime(new?Date()));
????????goodsVO.setGoodsType(1024);
????????goodsVO.setGoodsCode("123456789");
????????goodsVO.setGoodsName("我的測試商品");
????????cacheBuilder.set(cacheKey, goodsVO);
????????GoodsVO goodsVO1 = cacheBuilder.get(cacheKey);
????????Assert.assertNotNull(goodsVO1);
????????version = cacheBuilder.resetCacheVersion();
????????System.out.println(String.format("重置后的緩存版本:%s", version));
????????cacheKey = cacheBuilder.generateVerKey("goods112233");
????????cacheBuilder.set(cacheKey, goodsVO);
????????GoodsVO goodsVO2 = cacheBuilder.get(cacheKey);
????????Assert.assertNotNull(goodsVO2);
????????Assert.assertTrue("兩個緩存對象的主鍵相同", goodsVO1.getGoodsId().equals(goodsVO2.getGoodsId()));
????}
String?cacheKey = _cacheBuilder.generateVerKey("com.power.demo.apiservice.impl.getgoodsbyid."?+ request.getGoodsId());
GoodsVO?goodsVO = _cacheBuilder.get(cacheKey, _goodsService::getGoodsByGoodsId, request.getGoodsId());參考:
http://ifeve.com/google-guava/
http://www.cnblogs.com/luochengqiuse/p/4640932.html
往期推薦
我們在星球聊了很多深度話題,你不來看看?


我的星球是否適合你?
點擊閱讀原文看看我們都聊過啥?
評論
圖片
表情
