解決springCache配置中踩的坑
springCache配置中踩的坑
項目基于SpringBoot,使用了SpringCache。
早先在網(wǎng)上找了一份SpringCache的配置,后來由于需要使用到自定義序列化方法,注入一個自定義的序列化類。但是在后來發(fā)現(xiàn)自定義的序列化類始終沒有調(diào)用,后來查看源碼后終于發(fā)現(xiàn)了原因
先附上正確的配置
@Bean public CacheManager cacheManager(RedisConnectionFactory factory, SessionSerializer serializer) { logger.debug("生成緩存管理器"); logger.debug("注入的序列化工具={}", serializer); RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer); logger.debug("生成的cache序列化工具={}", pair); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一個默認(rèn)配置,通過config對象即可對緩存進(jìn)行自定義配置 config = config.entryTtl(Duration.ofMinutes(10)) // 設(shè)置緩存的默認(rèn)過期時間,也是使用Duration設(shè)置 .serializeValuesWith(pair) // .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .disableCachingNullValues() ; // 不緩存空值 logger.debug("初始化完成的config={}", config); // 設(shè)置一個初始化的緩存空間set集合 Set<String> cacheNames = new HashSet<>(); cacheNames.add(CACHE_NAME); return RedisCacheManager.builder(new CusTtlRedisCacheWriter(factory)) // 使用自定義的緩存配置初始化一個cacheManager .cacheDefaults(config)//這一句必須要最先執(zhí)行,否則實際運行時使用的是defaultConfig .initialCacheNames(cacheNames) // .withInitialCacheConfigurations(configMap) // .transactionAware() .build(); }
重要在于最后一行return的時候,早先的找到的資料說initialCacheNames方法一定要先執(zhí)行,否則就會巴拉巴拉~~~,,結(jié)果就掉坑了
如果initialCacheNames方法先執(zhí)行的話,實際上CacheManager里使用的是DefaultConfig,里面的序列化方式也就是Jdk序列化,后面在調(diào)用cacheDefaults也沒有用了。
所有,cacheDetaults方法一定要先執(zhí)行
springCache配置及一些問題的解決
配置
1. applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven /> <!-- 定義緩存管理 --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="activityCache"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="awardsCache"/> </set> </property> </bean>
Spring內(nèi)部默認(rèn)使用 ConcurrentHashMap 來存儲, 配置中的 activityCache awardsCache 就是一個一個的 ConcurrentHashMap 對象的名字. 另外 spring還支持使用 EHCache 來存儲緩存.
2. 在service的實現(xiàn)類上加上 @Cacheable
//@Service //public class LotteryActivityServiceImpl implements LotteryActivityService @Cacheable(value = "activityCache", key = "#shortName") public LotteryActivity findByShortName(String shortName) { log.info("query activity : {} from database.", shortName); return activityRepository.findByShortName(shortName); }
@Cacheable參數(shù)
value | 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 緩存的 key,可以為空,如果指定要按照 SpEL 表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合 例如: @Cacheable(value=”testcache”,key=”#userName”) |
condition | 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進(jìn)行緩存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
在需要清除緩存的方法上加上@CacheEvict
@CacheEvict(value="activityCache", allEntries = true, beforeInvocation = true) public void cleanActivityCache(String shortName) { log.info("cleaned cache activity : {}", shortName); }
@CacheEvict 參數(shù)
value | 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 例如: @CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”} |
key | 緩存的 key,可以為空,如果指定要按照 SpEL 表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合 例如: @CachEvict(value=”testcache”,key=”#userName” |
condition | 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才清空緩存 例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”) |
allEntries | 是否清空所有緩存內(nèi)容,缺省為 false,如果指定為 true,則方法調(diào)用后將立即清空所有緩存 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法執(zhí)行前就清空,缺省為 false,如果指定為 true,則在方法還沒有執(zhí)行的時候就清空緩存,缺省情況下,如果方法執(zhí)行拋出異常,則不會清空緩存 例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
當(dāng)需要保證方法被調(diào)用,又希望結(jié)果被緩存, 可以使用@CachePut
@CachePut(value="accountCache",key="#account.getName()")// 更新 accountCache 緩存 public Account updateAccount(Account account) { return updateDB(account); }
@CachePut 參數(shù)
value | 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 緩存的 key,可以為空,如果指定要按照 SpEL 表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合 例如: @Cacheable(value=”testcache”,key=”#userName”) |
condition | 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進(jìn)行緩存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
注解最好加在實現(xiàn)類而不是接口的方法上
Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Cache* annotation, as opposed to annotating interfaces. You certainly can place the @Cache* annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies. The fact that Java annotations are not inherited from interfaces means that if you are using class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"), then the caching settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a caching proxy, which would be decidedly bad.
帶有@Cache* 注解的方法不能被定義在調(diào)用該方法的類里, 比如 UserController要調(diào)用 findUserByName(String name), 且該方法有 @Cacheabele注解, 那么該方法就不能被定義在 UserController中
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual caching at runtime even if the invoked method is marked with @Cacheable - considering using the aspectj mode in this case.
@Cache*注解要加在 public 方法上
When using proxies, you should apply the @Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods as it changes the bytecode itself.
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis實現(xiàn)傳入多個參數(shù)的四種方法詳細(xì)講解
這篇文章主要介紹了Mybatis實現(xiàn)傳入多個參數(shù)的四種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01Springboot安全框架整合SpringSecurity實現(xiàn)方式
這篇文章主要介紹了Spring全家桶中Springboot安全框架整合SpringSecurity的實現(xiàn)方式,有需要的朋友可以借鑒參考下,希望可以有所幫助2021-09-09jdbc+jsp實現(xiàn)簡單員工管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了jdbc+jsp實現(xiàn)簡單員工管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02IDEA配置使用Maven Helper插件的方法(詳細(xì)配置)
這篇文章主要介紹了Maven Helper插件IDEA配置使用(詳細(xì)配置),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12