SpringBoot_Cache自定義使用SimpleCacheManager方式
SpringBoot_Cache自定義使用SimpleCacheManager
在SpringBoot_Cache中,會默認使用SimpleCacheManager。
但是筆者遇到一個問題,就是當前項目中也引用了redis的maven依賴,導致Cache使用了JedisManager,筆者還是想使用默認的SimpleCacheManager
這個時候就需要我們手動生成SimpleCacheManager的bean,則Cache會強制使用該bean
關于SpringBoot_Cache的使用筆者就不再贅述,讀者可參考網(wǎng)絡文章。
解決方案
創(chuàng)建一個Config類,然后主動生成SimpleCacheManager,代碼如下
@Configuration public class CacheManagerConfig { @Bean("cacheManager") // List<Cache>會主動搜索Cache的實現(xiàn)bean,并添加到caches中 public SimpleCacheManager cacheManager(List<Cache> caches){ SimpleCacheManager simpleCacheManager = new SimpleCacheManager(); simpleCacheManager.setCaches(caches); return simpleCacheManager; } @Bean("stockDetail") public ConcurrentMapCacheFactoryBean stockDetail(){ ConcurrentMapCacheFactoryBean stockDetail = new ConcurrentMapCacheFactoryBean(); // 如果用戶設置名稱為stockDetail的緩存,則需要添加這樣的一個bean stockDetail.setName("stockDetail"); return stockDetail; } @Bean("detailMsg") public ConcurrentMapCacheFactoryBean detailMsg(){ ConcurrentMapCacheFactoryBean stockDetail = new ConcurrentMapCacheFactoryBean(); // 如果用戶設置名稱為detailMsg的緩存,則需要添加這樣的一個bean stockDetail.setName("detailMsg"); return stockDetail; } }
注意:如果讀者在使用@Cache的時候,需要多個不同命名的cache時,需添加多個ConcurrentMapCacheFactoryBean
spring cache自定義spring cacheManager和cache
緩存 可以提高訪問速度,對高性能、高并發(fā)有一定的治療效果。
緩存從存儲位置可以分為 JVM級別緩存,進程級別緩存。JVM級別緩存優(yōu)點:訪問速度最快;缺點:不能實現(xiàn)分布式緩存,因為每個節(jié)點都是一個單獨的JVM實例,所以各個節(jié)點的緩存不可見,會導致緩存不一致。進程級別緩存優(yōu)點:保證緩存一致性,因為進程級別的緩存 相當于一個緩存服務器,各個節(jié)點都去同一個緩存服務器取數(shù)據(jù),所以緩存一致性有保證。進程級別緩存缺點:IO消耗,相對慢于JVM級別緩存,但還是比數(shù)據(jù)庫查詢快。
Spring 對 cache 進行了抽象 并提供注解。但沒有具體實現(xiàn)緩存。類似于JDBC接口的意思。定義了規(guī)范,讓小面的廠商進行具體的實現(xiàn)。其中定義了兩個主要的接口:緩存管理器接口:CacheManager 緩存接口:Cache
緩存管理器接口:CacheManager
接口很簡單 只有兩個方法:
public interface CacheManager { @Nullable Cache getCache(String name); Collection<String> getCacheNames(); }
Cache getCache(String name);
此方法可以定義根據(jù)緩存名稱獲取緩存,此方法可以實現(xiàn)二級緩存。
Collection<String> getCacheNames();
此方法獲取所有緩存名稱。
cacheManager是spring獲取cache的入口,所以你可以在getCache(String name) 方法中定義你的獲取cache的實現(xiàn)邏輯。
public class MyCacheManager implements CacheManager { private ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); public MyCacheManager(String name, Cache cache) { this.caches.put(name, cache); } @Override public Cache getCache(String name) { //更加緩存名字獲取緩存 Cache cache = this.caches.get(name); if (cache != null) { return cache; } else { //如果指定的緩存名字沒有找到 則新建一個ConcurrentMapCache 并保存在cachemanager中 ConcurrentMapCache newCache = new ConcurrentMapCache(name); caches.put(name, newCache); return newCache; } } @Override public Collection<String> getCacheNames() { return this.caches.keySet(); } }
自定義緩存實現(xiàn)
spring只是定義了緩存 并實現(xiàn)了幾個常用的cache比如目前較為流行的緩存有conCurrentMapCache、ehcache、caffeine。
而redis的實現(xiàn)由redis廠商提供。
其中conCurrentMapCache由spring實現(xiàn),在spring-context包中。ehcache、caffeine的實現(xiàn)放在了spring-context-support包下。
rediscache實現(xiàn)在 spring-data-redis包下
定義好cacheManager后將其加入spring容器管理。下面貼出緩存在spring配置文件中的配置有些bean是在接下來講的內(nèi)容。
<bean id="cacheManager" class="com.app.utils.cache.MyCacheManager"> <constructor-arg value="userCache" /> <!-- 緩存名稱 --> <constructor-arg ref="mapCaffeCache" /> <!-- 緩存實現(xiàn)類 --> </bean> <!-- 自定義緩存實現(xiàn) --> <bean id="mapCaffeCache" class="com.app.utils.cache.MapCaffeCache"></bean> <!-- 自定義緩存key實現(xiàn) --> <bean id="cacheKeyGenerator" class="com.app.utils.cache.CacheKeyGenerator"></bean> <!-- 自定義spring啟動加載bean時加載緩存 --> <bean id="cacheBeanPostProcessor" class="com.app.CacheBeanPostProcessor"></bean>
配置好bean后 在cache注解中 都會有cacheManager的指定 比如@cacheable注解 就有cacheManager的指定
@Cacheable(cacheManager = "cacheManager", key = "#i", value = "userCache", condition = "#i>7", unless = "#i>8") public String getList(int i) { return "list server method :: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); }
cache接口
public interface Cache { /** * Return the cache name. */ String getName(); /** * Return the underlying native cache provider. */ Object getNativeCache(); //獲取緩存的主要方法 @Nullable ValueWrapper get(Object key); @Nullable <T> T get(Object key, @Nullable Class<T> type); @Nullable <T> T get(Object key, Callable<T> valueLoader); //添加緩存的主要方法 void put(Object key, @Nullable Object value); @Nullable default ValueWrapper putIfAbsent(Object key, @Nullable Object value) { ValueWrapper existingValue = get(key); if (existingValue == null) { put(key, value); } return existingValue; } void evict(Object key); default boolean evictIfPresent(Object key) { evict(key); return false; } void clear(); default boolean invalidate() { clear(); return false; } @FunctionalInterface interface ValueWrapper { @Nullable Object get(); } @SuppressWarnings("serial") class ValueRetrievalException extends RuntimeException { @Nullable private final Object key; public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) { super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex); this.key = key; } @Nullable public Object getKey() { return this.key; } } }
cache的自定義實現(xiàn)類
public class MapCaffeCache implements Cache { // private ConcurrentMapCache mapCache = new ConcurrentMapCache("mapCache"); private com.github.benmanes.caffeine.cache.@NonNull Cache<Object, Object> mapCache = Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.SECONDS).expireAfterAccess(5, TimeUnit.SECONDS).maximumSize(5).build(); private com.github.benmanes.caffeine.cache.@NonNull Cache<Object, Object> caffeCache = Caffeine.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES).expireAfterAccess(1, TimeUnit.MINUTES).maximumSize(100).build(); @Autowired private StringRedisTemplate redisTemplate; private String name = "userCache"; @Override public String getName() { return this.name; } @Override public Object getNativeCache() { return this; } @Override public ValueWrapper get(Object key) { @Nullable Object ob = mapCache.getIfPresent(key); // 如果一級緩存有數(shù)據(jù) 直接返回 不觸發(fā)二級緩存 if (ob != null) { System.out.println(String.format("Cache L1 (CaffeineCache) :: %s = %s", key, ob)); SimpleValueWrapper valueWrapper = new SimpleValueWrapper(ob); return valueWrapper; } Object obj = caffeCache.getIfPresent(key); if (obj != null) { SimpleValueWrapper valueWrapper2 = new SimpleValueWrapper(obj); System.out.println(String.format("Cache L2 (CaffeineCache) :: %s = %s", key, obj)); // 如果二級緩存有數(shù)據(jù) 則更新到一級緩存 mapCache.put(key, obj); return valueWrapper2; } return null; } @Override public <T> T get(Object key, Class<T> type) { return (T) get(key).get(); } @Override public <T> T get(Object key, Callable<T> valueLoader) { return (T) get(key).get(); } @Override public void put(Object key, Object value) { mapCache.put(key, value); caffeCache.put(key, value); //當nginx搭建集群時 用redis 訂閱/發(fā)布 功能同步各項目點的緩存一致性 redisTemplate.convertAndSend("ch1", key + ":>" + value); } @Override public void evict(Object key) { mapCache.asMap().remove(key); caffeCache.asMap().remove(key); } @Override public void clear() { mapCache.asMap().clear(); caffeCache.asMap().clear(); } }
當cacheManager和cache實現(xiàn)完成后就是具體的cache注解使用了。主要有@Cacheable @Cacheput @CacheEvict @Caching @CacheConfig。主要講下@cacheable 其他注解設置都基本差不多。
@Cacheable用于獲取緩存數(shù)據(jù),如果緩存沒有命中則執(zhí)行被注解的方法 一般是執(zhí)行數(shù)據(jù)庫查詢,并剛數(shù)據(jù)庫查詢結(jié)果放入緩存中,cacheable緩存注解主要有一下設置:
@Cacheable(cacheManager = "cacheManager", key = "#i", value = "userCache", condition = "#i>7", unless = "#i>8")
- cacheManager :設置緩存管理器 相當于數(shù)據(jù)庫中的庫名稱
- value : 設置緩存名稱 相當于數(shù)據(jù)庫中的表
- key:設置緩存鍵名稱 緩存是一個key value存儲方式 所以可以是查找用的
- condition :設置滿足條件 true則緩存 false不緩存
- unless:設置不緩存條件 true不緩存 false 緩存
key的設置規(guī)則
- key = " 'key-name' " 寫死一個key 即 key-name 就是key的名字。注意要用單引號
- key="#id" 使用注解的方法中的參數(shù) id 的值作為key ,id 是方法參數(shù)列表的名字
- key="cacheKeyGenerator" cacheKeyGenerator為spring容器管理的自定義key生成器
自定義key生成器
實現(xiàn)KeyGenerator接口就可以自定義一個key生成方法
public class CacheKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { if (params.length == 0) { return "spring:cache:defaultKey"; } else { return target.getClass().getSimpleName() + ":" + method.getName(); } } }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot+vue項目從第一行代碼到上線部署全流程
本文詳細介紹了如何從零開始搭建一個基于Spring Boot和Vue.js的前后端分離項目,并涵蓋項目需求分析、技術(shù)選型、項目結(jié)構(gòu)設計、前后端交互、部署上線等全過程,感興趣的朋友跟隨小編一起看看吧2024-11-11java中ArrayList 、LinkList的區(qū)別分析
java中ArrayList 、LinkList的區(qū)別分析,需要的朋友可以參考一下2013-05-05java中\(zhòng)t,\n,\r,\b,\f 的作用及說明
這篇文章主要介紹了java中\(zhòng)t,\n,\r,\b,\f 的作用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07