Spring cache简单使用.docx

上传人:b****6 文档编号:7970257 上传时间:2023-01-27 格式:DOCX 页数:27 大小:35.78KB
下载 相关 举报
Spring cache简单使用.docx_第1页
第1页 / 共27页
Spring cache简单使用.docx_第2页
第2页 / 共27页
Spring cache简单使用.docx_第3页
第3页 / 共27页
Spring cache简单使用.docx_第4页
第4页 / 共27页
Spring cache简单使用.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

Spring cache简单使用.docx

《Spring cache简单使用.docx》由会员分享,可在线阅读,更多相关《Spring cache简单使用.docx(27页珍藏版)》请在冰豆网上搜索。

Spring cache简单使用.docx

Springcache简单使用

Springcache简单使用

前言

spring有一套和各种缓存的集成方式。

类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guavacache.

什么时候用缓存

首先,缓存是为了省略消耗时间的步骤,比如io。

当我需要从数据库查询的数据几乎没有变化,或者变化很少的时候,我就没必要每次都去数据库里拿数据了。

大可以放到本地,直接取出来就可以了。

这时候需要注意的是数据一致性问题,缓存的数据是否被更改了,数据是否有效。

我的项目是分布式部署的,但还没有搭建分布式缓存服务。

我采用的本地缓存,也就是说,我的缓存只能在本实例中,跨机器访问则不命中。

即便如此也大大减少了访问数据库的开销了。

配置缓存

这里采用guavacache作为本地缓存。

将guavacache注册到cacheManger里就可以调用了。

1.配置cacheManger

首先针对要缓存的类型,配置缓存策略。

这里设置最大缓存数量和缓存过期时间

publicstaticfinalStringHOTEL_POSTION="hotel_position";//cachekey

@Value("${cache.guavaCache.hotelPosition.maxSize}")

privatelonghotelPositionMaxSize;

@Value("${cache.guavaCache.hotelPosition.duration}")

privatelonghotelPositionDuration;

privateGuavaCachebuildHotelPositionCache(){

returnnewGuavaCache(HOTEL_POSTION,

CacheBuilder.newBuilder()

.recordStats()

.maximumSize(hotelPositionMaxSize)

.expireAfterWrite(hotelPositionDuration,TimeUnit.DAYS)

.build());

}

将刚才创建的缓存策略添加到cacheManger:

@Bean

publicCacheManagercacheManager(){

SimpleCacheManagermanager=newSimpleCacheManager();

Listlist=newArrayList();

list.add(buildHotelPositionCache());

manager.setCaches(list);

returnmanager;

}

2.配置要缓存的方法

在需要使用这个缓存的地方,增加一行注解

@Cacheable(value=CacheManagementConfig.HOTEL_POSTION,key="{#hotelId}",condition="",unless="!

#result.isSuccessful()")

publicBaseDomainResponsegetHotelPosition(inthotelId,StringapiToken){

//......

}

@Cacheable表示这个方法要被缓存

valuestring,表示这个方法缓存的唯一性标识,即这方法缓存的key。

语法为SpEL.

keyString,表示每条请求缓存的key,即如果key相同,则返回缓存中对应的数据

conditionboolean,可以额外添加缓存的条件.语法为SpEL.

unlessboolean,配置哪些条件下的记录不缓存。

语法为SpEL.

result表示return的这个对象,可以同result来调用这个对象的属性,比如isSuccessful()就是我返回对象的一个方法。

官方文档

此处学习官方文档cache部分,spring版本4.1+。

Atitscore,theabstractionappliescachingtoJavamethods,reducingthusthenumberofexecutionsbasedontheinformationavailableinthecache.Thatis,eachtimeatargetedmethodisinvoked,theabstractionwillapplyacachingbehaviorcheckingwhetherthemethodhasbeenalreadyexecutedforthegivenarguments.Ifithas,thenthecachedresultisreturnedwithouthavingtoexecutetheactualmethod;ifithasnot,thenmethodisexecuted,theresultcachedandreturnedtotheusersothat,thenexttimethemethodisinvoked,thecachedresultisreturned.Thisway,expensivemethods(whetherCPUorIObound)canbeexecutedonlyonceforagivensetofparametersandtheresultreusedwithouthavingtoactuallyexecutethemethodagain.Thecachinglogicisappliedtransparentlywithoutanyinterferencetotheinvoker.

这个缓存应用于java方法级别缓存,通过缓存中的数据来减少方法执行次数。

每当目标方法被调用,springcache会执行一个缓存行为来检查这个相同参数的方法是否已经被执行。

如果被执行过了,那么不执行方法直接返回缓存中的结果。

通过这样,代价高的方法(CPU或IO依赖)可以只执行一次,相同参数的结果会复用而不是真正的执行这个方法。

这个缓存逻辑对调用者来说是透明的,也就是调用者不用管这个缓存逻辑。

JustlikeotherservicesintheSpringFramework,thecachingserviceisanabstraction(notacacheimplementation)andrequirestheuseofanactualstoragetostorethecachedata-thatis,theabstractionfreesthedeveloperfromhavingtowritethecachinglogicbutdoesnotprovidetheactualstores.Thisabstractionismaterializedbytheorg.springframework.cache.Cacheandorg.springframework.cache.CacheManagerinterfaces.

Thereareafewimplementationsofthatabstractionavailableoutofthebox:

JDKjava.util.concurrent.ConcurrentMapbasedcaches,Ehcache2.x,Gemfirecache,Caffeine,GuavacachesandJSR-107compliantcaches(e.g.Ehcache3.x).SeeSection36.7,“Plugging-indifferentback-endcaches”formoreinformationonplugginginothercachestores/providers.

springcache是一个抽象的概念,没有提供实现方式去存储数据,开发者可以自己选择任意的实现。

比如DKjava.util.concurrent.ConcurrentMapbasedcaches,Ehcache2.x,Gemfirecache,Caffeine,GuavacachesandJSR-107compliantcaches(e.g.Ehcache3.x)。

Ifyouhaveamulti-processenvironment(i.e.anapplicationdeployedonseveralnodes),youwillneedtoconfigureyourcacheprovideraccordingly.Dependingonyourusecases,acopyofthesamedataonseveralnodesmaybeenoughbutifyouchangethedataduringthecourseoftheapplication,youmayneedtoenableotherpropagationmechanisms.

Cachingaparticularitemisadirectequivalentofthetypicalget-if-not-found-then-proceed-and-put-eventuallycodeblocksfoundwithprogrammaticcacheinteraction:

nolocksareappliedandseveralthreadsmaytrytoloadthesameitemconcurrently.Thesameappliestoeviction:

ifseveralthreadsaretryingtoupdateorevictdataconcurrently,youmayusestaledata.Certaincacheprovidersofferadvancedfeaturesinthatarea,refertothedocumentationofthecacheproviderthatyouareusingformoredetails.

Tousethecacheabstraction,thedeveloperneedstotakecareoftwoaspects:

cachingdeclaration-identifythemethodsthatneedtobecachedandtheirpolicy

cacheconfiguration-thebackingcachewherethedataisstoredandreadfrom

如果你采用多过程环境(比如,一个项目部署到多个服务节点,即分布式部署),你需要配置相应的的缓存实现。

在你的使用案例中,同样数据的拷贝已经足够使用了。

但如果你在这期间修改了数据,你需要使用其他传播机制来控制缓存的一致性。

缓存一个指定的条目直接等价于获取-如果-不存在-然后-执行-并且-最好放入缓存的程序逻辑的代码块:

不会阻塞并且多线程可以并发地加载相同的条目。

缓存更新策略也一样:

如果几个县城尝试并发地更新或者移除缓存的数据,你需要使用过期的数据。

在这个领域,特定的缓存实现提供更先进的方式,参考你使用的缓存实现的文档来获取等多的详情。

想要使用这个抽象的缓存,开发者需要关心两个方面:

缓存声明-定义需要被缓存的方法以及对应的缓存策略。

缓存配置-数据存储和读取的实现。

1.基于注解的声明式缓存

缓存抽象提供了一系列的java注解:

@Cacheable触发缓存逻辑

@CacheEvict触发缓存逐出逻辑

@CachePut不干涉方法执行地更新缓存

@Caching重组一个方法上的多重缓存操作

1.1@Cacheable注解

就像名字所暗示的,@Cacheable是用来区分方法是否可缓存的。

也就是说,哪个方法可以把结果存储到cache中,所以随后调用(相同的参数)时会返回cache中的值,而且并不会实际上运行这个method。

最简单的用法:

注解需要一个cache的name来关联这个method。

@Cacheable("books")

publicBookfindBook(ISBNisbn){...}

在上述的片段中,methodfindBook关联到名字叫做books的cache。

每次这个方法被调用的时候,cache会检查这个调用是否已经被执行过了并且不必重复执行。

大多数情况下,只声明一个cache,但这个注解支持声明多个name,因此可以使用多个cahce。

这样,在执行method之前每个cache都会检查是否存在-如果至少一个cache命中了,然后就会返回关联的值。

默认key注册模式

因为cache本质上是key-value存储,每次调用缓存的method需要被翻译成一个合适的key来获取缓存。

Outofthebox(这句话不知道该怎么翻译,box应该是指这个类,即在这个method所在的类之外),缓存代理(cacheabstraction)使用一个基于以下算法的简单的KeyGenerator:

如果没有参数,key就是SimpleKey.EMPTY.

如果只有一个参数,则返回那个参数.

如果多个参数,返回SimpleKey包含所有的参数

只要参数有__naturalkeys__并且实现了合法的hashCode()和equals(),这个方法适合于大多数使用案例。

如果不是,则key产生策略就需要改变。

不想使用默认的key生产机制,你需要实现接口:

org.springframework.cache.interceptor.KeyGenerator.

自定义Key产生声明

因为caching是普遍的,所以很可能目标method有各种签名(signatures)不可以简单的映射到cache结构中。

这个在目标mothod有多个参数但只有部分参数和缓存关联的时候就变得明显。

简单的说,cache默认把参数组合成一个key,这个key对应一个结果,下载遇到相同参数就会对应这个key,可以去除这个key对应的结果。

然而,有时候,我们有多个参数,比如a,b,c。

只有a和b和缓存的结果有关,c是变化的。

@Cacheable("books")

publicBookfindBook(ISBNa,Stringb,Stringtoken)

假设我这个findBook需要一个token来获取权限,但和book无关。

那么我们遇到相同的a就可以返回对应的book了,不需要关心token。

换句话说,自己可以定义缓存的条件,只要a和b相同,则命中同一个缓存。

然而,默认的会将a和b还有token组成一个key,只有这个三个相同的时候才会命中缓存。

这时候就需要我们自定义key的组成了。

以下示例各种SpEL声明,通过SpEL语法来声明key:

//仅仅使用key(isbn)

@Cacheable(cacheNames="books",key="#isbn")

publicBookfindBook(ISBNisbn,Stringb,Stringtoken)

//使用isbn的一个属性当做key

@Cacheable(cacheNames="books",key="#isbn.rawNumber")

publicBookfindBook(ISBNisbn,booleancheckWarehouse,booleanincludeUsed)

//调用某个类的某个方法来生成key

@Cacheable(cacheNames="books",key="T(someType).hash(#isbn)")

publicBookfindBook(ISBNisbn,booleancheckWarehouse,booleanincludeUsed)

//组合key(a和b)

@Cacheable(cacheNames="books",key="#a.concat(#b)")

publicBookfindBook(Stringa,Stringb,Stringtoken)

上述代码片段显示了选择一个特定的参数或者一个参数的属性或者任意的方法或者组合参数作为key是多么简单。

如果产生key的算法太特殊或者如果这个key需要共享,你可以自定义一个keyGenerator。

只要声明自定义的KeyGenerator的bean实现就可以了:

@Cacheable(cacheNames="books",keyGenerator="myKeyGenerator")

publicBookfindBook(ISBNisbn,booleancheckWarehouse,booleanincludeUsed)

`key`和`keyGenerator`是互斥的,如果同时声明会抛出异常。

默认的CacheResolution

Outofthebox,缓存代理使用简单的CacheResolver来获取cache,这个是可以使用CacheManager来手动配置的。

如果不想使用默认的cacheresolver,你需要实现接口:

org.springframework.cache.interceptor.CacheResolver

自定义CacheResolution

默认的cacheresolution适合于使用一个CacheManager并且没有复杂的cacheresolution.

对于采用多个cachemanagers的应用,要设置cacheManger:

@Cacheable(cacheNames="books",cacheManager="anotherCacheManager")

publicBookfindBook(ISBNisbn){...}

当然也可以完全替换CacheResolver,就像keygeneration一样简单。

每次cache操作都会请求这个resolution,基于运行时的参数来交给它的实现。

@Cacheable(cacheResolver="runtimeCacheResolver")

publicBookfindBook(ISBNisbn){...}

就像`key`和`keyGenerator`一样,`cacheManager`和`cacheResolver`参数也是互斥的,同时声明会抛出异常。

同步caching

在多线程环境,一个操作也许会并发的执行(比如启动的时候)。

默认的,cache代理不会lock并且同样的数据也许会计算多次,这与cache的目标相悖。

在这些特殊的场景,当计算的时候,参数sync可以用来通知将cachelockcacheentry.这样,只有一个线程可以计算,其他的等待entry被更新到cache。

@Cacheable(cacheNames="foos",sync="true")

publicFooexecuteExpensiveOperation(Stringid){...}

条件缓存(conditionalcaching)

有时候,一个method也许并不适合全部缓存(比如,根据参数缓存)。

cache注解通过参数condition来支持这种功能,同样使用SpEL表达式,结果为true或false,如果是true则缓存,否则表现为这个method没有缓存。

这个判断会在每次获取value的时候执行,无论缓存的value是什么以及无论使用哪个参数。

一个简单的示例,一下method只有在参数name长度小于32的时候执行缓存。

@Cacheable(cacheNames="book",condition="#name.length<32")

publicBookfindBook(Stringname)

除了使用condition,unless可以用来否决把结果加入缓存。

不同的是,unless的表达式会在method执行结束后考量,就是mehtod执行完后判断是否加入缓存。

扩展之前的示例--我们只需要缓存paperbackbooks.unless为true的时候不缓存。

@Cacheable(cacheNames="book",condition="#name.length<32",unless="#result.hardback")

publicBookfindBook(Stringname)

这里#result就是指向返回值。

可使用的SpEL表达式

每个SpEL表达式都有一个专门的context。

除了采用参数构建表达式,框架提供了专门的与caching相关的元数据,比如参数名。

下表列出了在context中可用的参数,你可以用来当做key和conditional处理。

NameLocationDescriptionExample

methodNamerootobject被执行的method的名字#root.methodName

methodrootobject被执行的method#root.method.name

targetrootobject执行的对象#root.target

targetClassrootobject执行对象的class#root.targetClass

argsrootobject执行对象的参数们(数组)#root.args[0]

cachesrootobject当前method对应的缓存集合#root.caches[0].name

argumentnameevaluationcontext任意method的参数。

如果特殊情况下参数还没有被赋值(e.g.没有debug信息),参数可以使用#a<#arg>来表示,其中#arg代表参数顺序,从0开始#iban或者#a0(也可以使用#p0或者#p<#arg>注解来启用别名)

resultevaluationcontextmethod执行的结果(要缓存的对象),仅仅在unless表达式中可以使用,或者cacheput(用来计算key),或者cacheevict表达式(当beforeInvocation=false).为了支持wrapper,比如Optional,#result指向世纪的对象,不是wrapper.

参考:

36.CacheAbstraction

36.1Int

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1