使用Spring Cache設(shè)置緩存條件操作
Spring Cache設(shè)置緩存條件
原理
從Spring3.1開(kāi)始,Spring框架提供了對(duì)Cache的支持,提供了一個(gè)對(duì)緩存使用的抽象,通過(guò)在既有代碼中添加少量它定義的各種 annotation,即能夠達(dá)到緩存方法的返回對(duì)象的作用。
提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具體見(jiàn)下表:
注解 | 說(shuō)明 |
---|---|
@Cacheable | 可以標(biāo)注在類(lèi)或方法上:標(biāo)注在方法上表示該方法支持?jǐn)?shù)據(jù)緩存;標(biāo)在類(lèi)上表示該類(lèi)的所有方法都支持?jǐn)?shù)據(jù)緩存。 具體功能:在執(zhí)行方法體之前,檢查緩存中是否有相同key值的緩存存在,如果存在對(duì)應(yīng)的緩存,直接返回緩存中的值;如果不存在對(duì)應(yīng)的緩存,則執(zhí)行相應(yīng)的方法體獲取數(shù)據(jù),并將數(shù)據(jù)存儲(chǔ)到緩存中。 |
@CachePut | 可以標(biāo)注在類(lèi)或方法上,表示支持?jǐn)?shù)據(jù)緩存。 具體功能:在方法執(zhí)行前不會(huì)檢查緩存中是否存在相應(yīng)的緩存,而是每次都會(huì)執(zhí)行方法體,并將方法執(zhí)行結(jié)果存儲(chǔ)到緩存中,如果相應(yīng)key值的緩存存在,則更新key對(duì)應(yīng)的value值。 |
@CacheEvict | 可以標(biāo)注在類(lèi)或方法上,用于清除相應(yīng)key值的緩存。 |
@Caching | 可以標(biāo)注在類(lèi)或方法上,它有三個(gè)屬性cacheable、put、evict分別用于指定@Cacheable、@CachePut和@CacheEvict |
當(dāng)需要在類(lèi)上或方法上同時(shí)使用多個(gè)注解時(shí),可以使用@Caching,如:
@Caching(cacheable=@Cacheable("User"), evict = {@CacheEvict("Member"), @CacheEvict(value = "Customer", allEntries = true)})
@Cacheable的常用屬性及說(shuō)明
如下表所示:
@Cacheable屬性 | 說(shuō)明 |
---|---|
key | 表示緩存的名稱(chēng),必須指定且至少要有一個(gè)值,比如:@Cacheable(value=“Dept”)或@Cacheable(value={“Dept”,“Depts”}) |
condition | 表示是否需要緩存,默認(rèn)為空,表示所有情況都會(huì)緩存。通過(guò)SpEL表達(dá)式來(lái)指定,若condition的值為true則會(huì)緩存,若為false則不會(huì)緩存,如@Cacheable(value=“Dept”,key="‘deptno_'+# deptno “,condition=”#deptno<=40") |
value | 表示緩存的key,支持SpEL表達(dá)式,如@Cacheable(value=“Dept”,key="‘deptno_' +#deptno"),可以不指定值,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合。除了上述使用方法參數(shù)作為key之外,Spring還提供了一個(gè)root對(duì)象用來(lái)生成key,使用方法如下表所示,其中"#root"可以省略。 |
Root對(duì)象
Root對(duì)象 | 說(shuō)明 |
---|---|
methodName | 當(dāng)前方法名,比如#root.methodName |
method | 當(dāng)前方法,比如#root.method.name |
target | 當(dāng)前被調(diào)用的對(duì)象,比如#root.target |
targetClass | 當(dāng)前被調(diào)用的對(duì)象的class,比如#root.targetClass |
args | 當(dāng)前方法參數(shù)組成的數(shù)組,比如#root.args[0] |
caches | 當(dāng)前被調(diào)用的方法使用的緩存,比如#root.caches[0].name |
@CachePut的常用屬性同@Cacheable
@CacheEvict的常用屬性如下表所示:
@CacheEvict屬性 | 說(shuō)明 |
---|---|
value | 表示要清除的緩存名 |
key | 表示需要清除的緩存key值, |
condition | 當(dāng)condition的值為true時(shí)才清除緩存 |
allEntries | 表示是否需要清除緩存中的所有元素。默認(rèn)為false,表示不需要,當(dāng)指定了allEntries為true時(shí),將忽略指定的key。 |
beforeInvocation | 清除操作默認(rèn)是在方法成功執(zhí)行之后觸發(fā)的,即方法如果因?yàn)閽伋霎惓6茨艹晒Ψ祷貢r(shí)不會(huì)觸發(fā)清除操作。使用beforeInvocation可以改變觸發(fā)清除操作的時(shí)間,當(dāng)該屬性值為true時(shí),會(huì)在調(diào)用該方法之前清除緩存中的指定元素。 |
示例:設(shè)置當(dāng) dname 的長(zhǎng)度大于3時(shí)才緩存
//條件緩存 @ResponseBody @GetMapping("/getLocByDname") @Cacheable(cacheNames = "dept", key = "#dname", condition = "#dname.length()>3") public String getLocByDname(@RequestParam("dname") String dname) {//key動(dòng)態(tài)參數(shù) QueryWrapper<Dept> queryMapper = new QueryWrapper<>(); queryMapper.eq("dname", dname); Dept dept = deptService.getOne(queryMapper); return dept.getLoc(); }
示例:unless 即條件不成立時(shí)緩存
#result 代表返回值,意思是當(dāng)返回碼不等于 200 時(shí)不緩存,也就是等于 200 時(shí)才緩存。
@ResponseBody @GetMapping("/getDeptByDname") @Cacheable(cacheNames = "dept", key = "#dname", unless = "#result.code != 200") public Result<Dept> getDeptByDname(@RequestParam("dname") String dname){//key動(dòng)態(tài)參數(shù) QueryWrapper<Dept> queryMapper = new QueryWrapper<>(); queryMapper.eq("dname", dname); Dept dept = deptService.getOne(queryMapper); if (dept == null) return ResultUtil.error(120, "dept is null"); else return ResultUtil.success(dept); }
Cache緩存配置
1、pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- 反射工具類(lèi)用于手動(dòng)掃描指定包下的注解,根據(jù)defaultCache模塊增加ehcache緩存域(非Spring Cache必須)--> <!-- https://mvnrepository.com/artifact/org.reflections/reflections --> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
2、Ehcache配置文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- 磁盤(pán)緩存位置 --> <diskStore path="java.io.tmpdir" /> <!-- name:緩存名稱(chēng)。 maxElementsInMemory:緩存最大個(gè)數(shù)。 eternal:對(duì)象是否永久有效,一但設(shè)置了,timeout將不起作用。 timeToIdleSeconds:設(shè)置對(duì)象在失效前的允許閑置時(shí)間(單位:秒)。僅當(dāng)eternal=false對(duì)象不是永久有效時(shí)使用,可選屬性,默認(rèn)值是0,也就是可閑置時(shí)間無(wú)窮大。 timeToLiveSeconds:設(shè)置對(duì)象在失效前允許存活時(shí)間(單位:秒)。最大時(shí)間介于創(chuàng)建時(shí)間和失效時(shí)間之間。僅當(dāng)eternal=false對(duì)象不是永久有效時(shí)使用,默認(rèn)是0.,也就是對(duì)象存活時(shí)間無(wú)窮大。 overflowToDisk:當(dāng)內(nèi)存中對(duì)象數(shù)量達(dá)到maxElementsInMemory時(shí),Ehcache將會(huì)對(duì)象寫(xiě)到磁盤(pán)中。 diskSpoolBufferSizeMB:這個(gè)參數(shù)設(shè)置DiskStore(磁盤(pán)緩存)的緩存區(qū)大小。默認(rèn)是30MB。每個(gè)Cache都應(yīng)該有自己的一個(gè)緩沖區(qū)。 maxElementsOnDisk:硬盤(pán)最大緩存?zhèn)€數(shù)。 diskPersistent:是否緩存虛擬機(jī)重啟期數(shù)據(jù) Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds:磁盤(pán)失效線程運(yùn)行時(shí)間間隔,默認(rèn)是120秒。 memoryStoreEvictionPolicy:當(dāng)達(dá)到maxElementsInMemory限制時(shí),Ehcache將會(huì)根據(jù)指定的策略去清理內(nèi)存。默認(rèn)策略是LRU(最近最少使用)。你可以設(shè)置為FIFO(先進(jìn)先出)或是LFU(較少使用)。 clearOnFlush:內(nèi)存數(shù)量最大時(shí)是否清除。 --> <!-- 默認(rèn)緩存 --> <defaultCache eternal="false" maxElementsInMemory="200000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" /> </ehcache>
3、配置類(lèi)
@Configuration @EnableCaching public class CustomConfiguration { /** * @see org.springframework.cache.interceptor.SimpleKeyGenerator * Generate a key based on the specified parameters. */ public static Object generateKey(Object... params) { if (params.length == 0) { return SimpleKey.EMPTY; } if (params.length == 1) { Object param = params[0]; if (param != null && !param.getClass().isArray()) { return param; } } return new SimpleKey(params); } /** * 若將target作為key的一部分時(shí),CGLIB動(dòng)態(tài)代理可能導(dǎo)致重復(fù)緩存 * 注意:返回的key一定要重寫(xiě)hashCode()和toString(),防止key對(duì)象不一致導(dǎo)致的緩存無(wú)法命中 * 例如:ehcache 底層存儲(chǔ)net.sf.ehcache.store.chm.SelectableConcurrentHashMap#containsKey */ @Bean public KeyGenerator customKeyGenerator(){ return (target, method, params) -> { final Object key = generateKey(params); StringBuffer buffer = new StringBuffer(); buffer.append(method.getName()); buffer.append("::"); buffer.append(key.toString()); // 注意一定要轉(zhuǎn)為String,否則ehcache key對(duì)象可能不一樣,導(dǎo)致緩存無(wú)法命中 return buffer.toString(); }; } /** * redis緩存管理器 */ @Bean @ConditionalOnBean(RedisConfiguration.class) @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "redis", matchIfMissing = false) public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) .entryTtl(Duration.ofMinutes(10)); return RedisCacheManager .builder(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory)) .cacheDefaults(config).build(); } /** * ehcache緩存管理器(默認(rèn)) * default XML files {@link net.sf.ehcache.config.ConfigurationFactory#parseConfiguration()} */ @Bean @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "ehcache", matchIfMissing = true) public CacheManager ehcacheCacheManager() { net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.create(); /** * 包掃描查找指定注解并將cacheNames添加到net.sf.ehcache.CacheManager(單例) */ Reflections reflections = new Reflections("com.example.demo.service", new TypeAnnotationsScanner() , new SubTypesScanner(), new MethodAnnotationsScanner()); Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(CacheConfig.class); for (Class<?> aClass : classesList) { final CacheConfig config = AnnotationUtils.findAnnotation(aClass, CacheConfig.class); if (config.cacheNames() != null && config.cacheNames().length > 0) { for (String cacheName : config.cacheNames()) { cacheManager.addCacheIfAbsent(cacheName); } } } /** * 方法級(jí)別的注解 @Caching、@CacheEvict、@Cacheable、@CachePut,結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景僅掃描@Cacheable即可 */ final Set<Method> methods = reflections.getMethodsAnnotatedWith(Cacheable.class); for (Method method : methods) { final Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class); if (cacheable.cacheNames() != null && cacheable.cacheNames().length > 0) { for (String cacheName : cacheable.cacheNames()) { cacheManager.addCacheIfAbsent(cacheName); } } } EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(); ehCacheCacheManager.setCacheManager(cacheManager); return ehCacheCacheManager; } }
4、示例
@Component @CacheConfig(cacheNames = "XXXServiceImpl", keyGenerator = "customKeyGenerator") public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXXEntity> implements XXXService { @CacheEvict(allEntries = true) public void evictAllEntries() {} @Override @Cacheable public List<XXXEntity> findById(Long id) { return this.baseMapper.selectList(new QueryWrapper<XXXEntity>().lambda() .eq(XXXEntity::getId, id)); } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring boot結(jié)合Redis實(shí)現(xiàn)工具類(lèi)的方法示例
這篇文章主要介紹了spring boot結(jié)合Redis實(shí)現(xiàn)工具類(lèi)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11java接收文件流+response.body()調(diào)用兩次問(wèn)題(分別接收文件和對(duì)象)
這篇文章主要介紹了java接收文件流+response.body()調(diào)用兩次問(wèn)題(分別接收文件和對(duì)象),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06spring Boot與Mybatis整合優(yōu)化詳解
關(guān)于spring-boot與mybatis整合優(yōu)化方面的介紹,就是Mybatis-Spring-boot-starter的介紹,具體內(nèi)容詳情大家參考下本文2017-07-07Java枚舉的七種常見(jiàn)用法總結(jié)(必看)
下面小編就為大家?guī)?lái)一篇Java枚舉的七種常見(jiàn)用法總結(jié)(必看)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11Springboot整合mybatisplus時(shí),使用條件構(gòu)造器排序報(bào)錯(cuò)問(wèn)題及解決
這篇文章主要介紹了Springboot整合mybatisplus時(shí),使用條件構(gòu)造器排序報(bào)錯(cuò)問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04Java數(shù)組的動(dòng)態(tài)初始化和常見(jiàn)問(wèn)題解析
本文介紹了數(shù)組動(dòng)態(tài)初始化的概念,即在初始化時(shí)僅指定數(shù)組長(zhǎng)度,系統(tǒng)會(huì)為數(shù)組分配初始值,而靜態(tài)初始化則手動(dòng)指定數(shù)組元素,系統(tǒng)根據(jù)元素個(gè)數(shù)計(jì)算數(shù)組長(zhǎng)度,這兩種初始化方式應(yīng)用場(chǎng)景不同,另外,還講述了數(shù)組默認(rèn)初始化值的規(guī)律及數(shù)組常見(jiàn)問(wèn)題,如越界問(wèn)題等2024-10-10