解決springCache配置中踩的坑
springCache配置中踩的坑
項(xiàng)目基于SpringBoot,使用了SpringCache。
早先在網(wǎng)上找了一份SpringCache的配置,后來(lái)由于需要使用到自定義序列化方法,注入一個(gè)自定義的序列化類。但是在后來(lái)發(fā)現(xiàn)自定義的序列化類始終沒(méi)有調(diào)用,后來(lái)查看源碼后終于發(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(); // 生成一個(gè)默認(rèn)配置,通過(guò)config對(duì)象即可對(duì)緩存進(jìn)行自定義配置
config = config.entryTtl(Duration.ofMinutes(10)) // 設(shè)置緩存的默認(rèn)過(guò)期時(shí)間,也是使用Duration設(shè)置
.serializeValuesWith(pair)
// .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.disableCachingNullValues()
; // 不緩存空值
logger.debug("初始化完成的config={}", config);
// 設(shè)置一個(gè)初始化的緩存空間set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add(CACHE_NAME);
return RedisCacheManager.builder(new CusTtlRedisCacheWriter(factory)) // 使用自定義的緩存配置初始化一個(gè)cacheManager
.cacheDefaults(config)//這一句必須要最先執(zhí)行,否則實(shí)際運(yùn)行時(shí)使用的是defaultConfig
.initialCacheNames(cacheNames)
// .withInitialCacheConfigurations(configMap)
// .transactionAware()
.build();
}
重要在于最后一行return的時(shí)候,早先的找到的資料說(shuō)initialCacheNames方法一定要先執(zhí)行,否則就會(huì)巴拉巴拉~~~,,結(jié)果就掉坑了
如果initialCacheNames方法先執(zhí)行的話,實(shí)際上CacheManager里使用的是DefaultConfig,里面的序列化方式也就是Jdk序列化,后面在調(diào)用cacheDefaults也沒(méi)有用了。
所有,cacheDetaults方法一定要先執(zhí)行
springCache配置及一些問(wèn)題的解決
配置
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 來(lái)存儲(chǔ), 配置中的 activityCache awardsCache 就是一個(gè)一個(gè)的 ConcurrentHashMap 對(duì)象的名字. 另外 spring還支持使用 EHCache 來(lái)存儲(chǔ)緩存.
2. 在service的實(shí)現(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 配置文件中定義,必須指定至少一個(gè) 例如:@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 配置文件中定義,必須指定至少一個(gè) 例如: @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,則在方法還沒(méi)有執(zhí)行的時(shí)候就清空緩存,缺省情況下,如果方法執(zhí)行拋出異常,則不會(huì)清空緩存 例如: @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 配置文件中定義,必須指定至少一個(gè) 例如: @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”) |
注解最好加在實(shí)現(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.
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java之MyBatis的Dao方式以及Dao動(dòng)態(tài)代理詳解
這篇文章主要介紹了Java之MyBatis的Dao方式以及Dao動(dòng)態(tài)代理詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
詳解SpringBoot與SpringCloud的版本對(duì)應(yīng)詳細(xì)版
這篇文章主要介紹了詳解SpringBoot與SpringCloud的版本對(duì)應(yīng)詳細(xì)版,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
SpringBoot 項(xiàng)目如何在tomcat容器中運(yùn)行的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot 項(xiàng)目如何在tomcat容器中運(yùn)行的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
JAVA內(nèi)存空間相關(guān)知識(shí)匯總
這篇文章主要介紹了JAVA內(nèi)存空間相關(guān)知識(shí),文中介紹的非常詳細(xì),代碼幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
測(cè)試springboot項(xiàng)目出現(xiàn)Test Ignored的解決
這篇文章主要介紹了測(cè)試springboot項(xiàng)目出現(xiàn)Test Ignored的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

