沒想到SpringCache還可以這麼玩

沒想到SpringCache還可以這麼玩

如果在工作遇到依賴系統返回耗時十分長,有時還不穩定,但是返回的結果數據又變化不大,這時我們該怎麼辦?下面我向大家推薦一下,使用SpringCache來構建方法級緩存,讓我們的服務變的順暢起來,同時提高我們服務的響應速度。在進行SpringCache實戰之前先給大家梳理一下思路。

使用SpringCache的思路

1.使用@EnableCaching或者來開啟緩存服務

2.實現Cache接口來構建自己的緩存處理器(實現查詢和添加的功能)

3.可以使用SimpleCacheManager緩存管理器,也可以繼承AbstractCacheManager來實現自己的緩存管理器

4.將緩存處理器註冊到緩存管理器中,由緩存管理器來統一管理緩存

5.在方法上可以使用@Cacheable,@CachePut,@CacheEvict加載方法上

  • @Cacheable 用於先查後做,如果根據相應的key查到結果就直接返回, 不在執行方法, 如果沒有則執行完方法後將方法的返回結果放入到緩存中
  • @CachePut 用於設置緩存,先執行方法,再緩存方法的結果
  • @CacheEvict用於刪除緩存。在方法執行前,刪除緩存

SpringCache實戰

我們這使用數據庫來當緩存的中間件,如果想使用Redis或者其他的中間件只需替換數據的實現或者重新實現緩存處理器,然後在用的使用指定緩存名稱即可。

1.定義緩存處理器

@Slf4j
public class DBCache implements Cache {
 /**
 * 緩存的命名屬性
 **/
 private String name;
 @Autowired
 private DBCacheRepository dbCacheRepository;
 
 @Override
 public String getName() {
 return this.name;
 }
 @Override
 public Object getNativeCache() {
 return this.dbCacheRepository;
 }
 @Override
 //查詢方法
 public ValueWrapper get(Object key) {
 ValueWrapper result = null;
 try {
 //查詢緩存中間件
 DBCachePO dbCachePO = dbCacheRepository.get(key.toString());
 String value = dbCachePO == null ? null : dbCachePO.getValue();
 //處理緩存中間件返回的值
 if (value != null) {
 Class valueType = Class.forName(dbCachePO.getValueClassType());
 result = new SimpleValueWrapper(new Gson().fromJson(value, valueType));
 }
 } catch (Exception e) {
 log.warn("get value from DBCache error, key={}", key, e);
 }
 return result;
 }
 
 //添加方法
 @Override
 public void put(Object key, Object value) {
 try {
 String[] keyArray = key.toString().split("#");
 if (ArrayUtils.isEmpty(keyArray) || keyArray.length < 2) {
 throw new RuntimeException("illegalKey, use: className.methodName#businessId#other, currentKey=" + key.toString());
 }
 String businessId = keyArray[1];
 DBCachePO dbCachePO = new DBCachePO();
 dbCachePO.setBusinessId(businessId);
 dbCachePO.setKey(key.toString());
 dbCachePO.setValue(new Gson().toJson(value));
 dbCachePO.setValueClassType(value.getClass().getTypeName());
 dbCacheRepository.add(dbCachePO);
 } catch (Exception e) {
 log.warn("DBCache set value error, key={}, value={}", key, value, e);
 }
 }
 
 public void setName(String name) {
 this.name = name;
 }
}

2.開啟緩存和將緩存處理器註冊到緩存管理器

@EnableCaching
@Configuration
public class CacheConfig {
 @Bean
 public DBCache dbCache(){
 DBCache dbCache = new DBCache();
 dbCache.setName("dbCache");
 return dbCache;
 }
 @Bean
 public SimpleCacheManager cacheManager(){
 SimpleCacheManager selfCacheManager = new SimpleCacheManager();
 Set caches = new HashSet<>();
 caches.add(dbCache());
 selfCacheManager.setCaches(caches);
 return selfCacheManager;
 }
}

3.應用到方法上,構建方法級緩存,在cacheNames中可以指定你想使用的緩存處理器,這裡對應的值就是緩存處理器裡name的值,key值的生成支持SpringEL(SpEL)

@Service
@Slf4j
public class UserService {
 
 @Cacheable(cacheNames = {"dbCache"},key = "#root.targetClass.simpleName + '.' + #root.methodName + '#' + #id")
 public UserDto getUserFromUserCenter(Long id){
 //模擬調用RPC
 log.info("========================RPC調用開始===================");
 UserDto userDto = buildUserDto();
 log.info("========================RPC調用完成===================");
 return userDto;
 }
 private UserDto buildUserDto(){
 UserDto userDto = new UserDto();
 userDto.setAge(18);
 userDto.setPassword("121321");
 userDto.setUserName("txw");
 userDto.setPhone("211656778");
 return userDto;
 }

效果顯示

1.插入數據庫的結構,如下圖所示:

沒想到SpringCache還可以這麼玩

2.代碼執行過程剖析圖

沒想到SpringCache還可以這麼玩

緩存存在時的執行圖,直接返回

沒想到SpringCache還可以這麼玩

緩存不存在的執行圖,先執行方法,在緩存結果

附數據庫腳本

CREATE TABLE `db_cache_record` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Id',
 `business_id` char(30) NOT NULL COMMENT '業務ID',
 `key` varchar(200) NOT NULL COMMENT 'key',
 `value` text COMMENT 'Value',
 `value_class_type` varchar(200) NOT NULL COMMENT '緩存value的java類型,用於反序列化',
 `add_time` datetime DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `key_UNIQUE` (`key`),
 KEY `idx_business_id` (`business_id`),
 KEY `idx_addtime` (`add_time`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;


分享到:


相關文章: