cacheable更新_Spring之缓存注解@Cacheable
从3.1开始,Spring引⼊了对Cache的⽀持。其使⽤⽅法和原理都类似于Spring对事务管理的⽀持。Spring Cache是作⽤在⽅法上的,其核⼼思想是这样的:当我们在调⽤⼀个缓存⽅法时会把该⽅法参数和返回结果作为⼀个键值对存放在缓存中,等到下次利⽤同样的参数来调⽤该⽅法时将不再执⾏该⽅法,⽽是直接从缓存中获取结果进⾏返回。所以在使⽤Spring Cache的时候我们要保证我们缓存的⽅法对于相同的⽅法参数要有相同的返回结果。
使⽤Spring Cache需要我们做两⽅⾯的事:
n 声明某些⽅法使⽤缓存
n 配置Spring对Cache的⽀持
和Spring对事务管理的⽀持⼀样,Spring对Cache的⽀持也有基于注解和基于XML配置两种⽅式。下⾯我们先来看看基于注解的⽅式。
1 基于注解的⽀持
Spring为我们提供了⼏个注解来⽀持Spring Cache。其核⼼主要是@Cacheable和@CacheEvict。使⽤
@Cacheable标记的⽅法在执⾏后Spring Cache将缓存其返回结果,⽽使⽤@CacheEvict标记的⽅法会在⽅法执⾏前或者执⾏后移除Spring Cache中的某些元素。下⾯我们将来详细介绍⼀下Spring基于注解对Cache的⽀持所提供的⼏个注解。
1.1 @Cacheable
@Cacheable可以标记在⼀个⽅法上,也可以标记在⼀个类上。当标记在⼀个⽅法上时表⽰该⽅法是⽀持缓存的,当标记在⼀个类上时则表⽰该类所有的⽅法都是⽀持缓存的。对于⼀个⽀持缓存的⽅法,Spring会在其被调⽤后将其返回值缓存起来,以保证下次利⽤同样的参数来执⾏该⽅法时可以直接从缓存中获取结果,⽽不需要再次执⾏该⽅法。Spring在缓存⽅法的返回值时是以键值对进⾏缓存的,值就是⽅法的返回结果,⾄于键的话,Spring⼜⽀持两种策略,默认策略和⾃定义策略,这个稍后会进⾏说明。需要注意的是当⼀个⽀持缓存的⽅法在对象内部被调⽤时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。
1.1.1 value属性指定Cache名称
value属性是必须指定的,其表⽰当前⽅法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是⼀个Cache也可以是多个Cache,当需要指定多个Cache时其是⼀个数组。
@Cacheable("cache1")//Cache是发⽣在cache1上的
publicUser find(Integer id) {
returnnull;
}
@Cacheable({"cache1", "cache2"})//Cache是发⽣在cache1和cache2上的
publicUser find(Integer id) {
returnnull;
}
1.1.2 使⽤key属性⾃定义key
key属性是⽤来指定Spring缓存⽅法的返回结果时对应的key的。该属性⽀持SpringEL表达式。当我们没有指定该属性时,Spring将使⽤默认策略⽣成key。我们这⾥先来看看⾃定义策略,⾄于默认策略会在后⽂单独介绍。
⾃定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这⾥的EL表达式可以使⽤⽅法参
数及它们对应的属性。使⽤⽅法参数时我们可以直接使⽤“#参数名”或者“#p参数index”。下⾯是⼏个使⽤参数作为key的⽰例。
@Cacheable(value="users", key="#id")publicUser find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#p0")publicUser find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#user.id")publicUser find(User user) {
returnnull;
}
@Cacheable(value="users", key="#p0.id")publicUser find(User user) {
returnnull;
}
除了上述使⽤⽅法参数作为key之外,Spring还为我们提供了⼀个root对象可以⽤来⽣成key。通过该root对象我们可以获取到以下信息。属性名称
描述
⽰例
methodName
当前⽅法名
#hodName
method
当前⽅法
#hod.name
target
当前被调⽤的对象
#root.target
targetClass
当前被调⽤的对象的class
#root.targetClass
args
当前⽅法参数组成的数组
#root.args[0]
caches
当前被调⽤的⽅法使⽤的Cache
#root.caches[0].name
当我们要使⽤root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使⽤的就是root对象的属性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name")publicUser find(User user) {
returnnull;
@Cacheable(value = "page_user",key ="T(String).valueOf(#page).concat('-').concat(#pageSize)",unless = "#result=null")//由于page是int型,concat要求变量必须为String,所以强转⼀下
@Overridepublic List page(int page, intpageSize) {returnuserMapper.page(page,pageSize);
}
1.1.3 condition属性指定发⽣的条件
有的时候我们可能并不希望缓存⼀个⽅法所有的返回结果。通过condition属性可以实现这⼀功能。con
dition属性默认为空,表⽰将缓存所有的调⽤情形。其值是通过SpringEL表达式来指定的,当为true时表⽰进⾏缓存处理;当为false时表⽰不进⾏缓存处理,即每次调⽤该⽅法时该⽅法都会执⾏⼀次。如下⽰例表⽰只有当user的id为偶数时才会进⾏缓存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")publicUser find(User user) {
System.out.println("find user by user " +user);returnuser;
}
1.1.4缓存的同步 sync:
在多线程环境下,某些操作可能使⽤相同参数同步调⽤。默认情况下,缓存不锁定任何资源,可能导致多次计算,⽽违反了缓存的⽬的。对于这些特定的情况,属性 sync 可以指⽰底层将缓存锁住,使只有⼀个线程可以进⼊计算,⽽其他线程堵塞,直到返回结果更新到缓存中。例:
@Cacheable(cacheNames="foos", sync="true")
public Foo executeExpensiveOperation(String id) {...}
1.2 @CachePut
在⽀持Spring Cache的环境下,对于使⽤@Cacheable标注的⽅法,Spring在每次执⾏前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执⾏该⽅法,⽽是直接从缓存中获取结果进⾏返回,否则才会执⾏并将返回结果存⼊指定的缓存中。@CachePut也可以声明⼀个⽅法⽀持缓存功能。与@Cacheable不同的是使⽤@CachePut标注的⽅法在执⾏前不会去检查缓存中是否存在之前执⾏过的结果,⽽是每次都会执⾏该⽅法,并将执⾏结果以键值对的形式存⼊指定的缓存中。
@CachePut也可以标注在类上和⽅法上。使⽤@CachePut时我们可以指定的属性跟@Cacheable是⼀样的。
@CachePut("users")//每次都会执⾏⽅法,并将结果存⼊指定的缓存中
publicUser find(Integer id) {
returnnull;
}
1.3 @CacheEvict
@CacheEvict是⽤来标注在需要清除缓存元素的⽅法或类上的。当标记在⼀个类上时表⽰其中所有的⽅法的执⾏都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与
@Cacheable对应的属性类似。即value表⽰清除操作是发⽣在哪些Cache上的(对应Cache的名称);key表⽰需要清除的是哪个key,如未指定则会使⽤默认策略⽣成的key;condition表⽰清除操作发⽣的条件。下⾯我们来介绍⼀下新出现的两个属性allEntries和beforeInvocation。
1.3.1 allEntries属性
allEntries是boolean类型,表⽰是否需要清除缓存中的所有元素。默认为false,表⽰不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache⼀下清除所有的元素,这⽐⼀个⼀个清除元素更有效率。
@CacheEvict(value="users", allEntries=true)public voiddelete(Integer id) {
System.out.println("delete user by id: " +id);
1.3.2 beforeInvocation属性
清除操作默认是在对应⽅法成功执⾏之后触发的,即⽅法如果因为抛出异常⽽未能成功返回时也不会触发清除操作。使⽤beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调⽤该⽅法之前清除缓存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true)public voiddelete(Integer id) {
System.out.println("delete user by id: " +id);
}
其实除了使⽤@CacheEvict清除缓存元素外,当我们使⽤Ehcache作为实现时,我们也可以配置Ehcache⾃⾝的驱除策略,其是通过Ehcache的配置⽂件来指定的。由于Ehcache不是本⽂描述的重点,这⾥就不多赘述了,想了解更多关于Ehcache的信息,请查看我关于Ehcache的专栏。
1.4 @Caching
@Caching注解可以让我们在⼀个⽅法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别⽤于指定@Cacheable、@CachePut和@CacheEvict。
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value= "cache3", allEntries = true) })publicUser find(Integer id) {
returnnull;
}
如下,Caching可以删除不同的key
@CacheConfig(cacheNames = { "userFocus"})public interfaceUserFocusMapper {
@CacheEvict(value="userFocusList",key = "#p0.userCode")intinsert(UserFocus userFocus);
@Cacheable(key= "#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType",unless="#result == null")
UserFocus findFocus(UserFocus userFocus);
el表达式获取值@Cacheable(value="userFocusList",key = "#p0.userCode",unless="#result == null||#result.size() == 0")
ListfindFocusList(UserFocus userCode);//利⽤@Caching中evict指定两个value下的不同key删除。
@Caching(evict = {@CacheEvict(value="userFocusList",key = "#p0.userCode"),
@CacheEvict(value="userFocus",key =
"#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType")})voidupdateUserFocus(UserFocus userFocus);
}
1.5 @CacheConfig
有时候⼀个类中可能会有多个缓存操作,⽽这些缓存操作可能是重复的。这个时候可以使⽤@CacheConfig
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
@CacheConfig是⼀个类级别的注解,允许共享缓存的名称、KeyGenerator、CacheManager 和CacheResolver。该操作会被覆盖。
1.6 使⽤⾃定义注解
Spring允许我们在配置可缓存的⽅法时使⽤⾃定义的注解,前提是⾃定义的注解上必须使⽤对应的注解进⾏标注。如我们有如下这么⼀个使⽤@Cacheable进⾏标注的⾃定义注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")public @interfaceMyCacheable {
}
那么在我们需要缓存的⽅法上使⽤@MyCacheable进⾏标注也可以达到同样的效果。
@MyCacheablepublicUser findById(Integer id) {
System.out.println("find user by id: " +id);
User user= newUser();
user.setId(id);
user.setName("Name" +id);returnuser;
}
2 配置Spring对Cache的⽀持
2.1 声明对Cache的⽀持
2.1.1 基于注解
配置Spring对基于注解的Cache的⽀持,⾸先我们需要在Spring的配置⽂件中引⼊cache命名空间,其次通过就可以启⽤Spring对基于注解的Cache的⽀持。
有⼀个cache-manager属性⽤来指定当前所使⽤的CacheManager对应的bean的名称,默认是cacheManager,所以当我们的CacheManager的id为cacheManager时我们可以不指定该参数,否则就需要我们指定了。
还可以指定⼀个mode属性,可选值有proxy和aspectj。默认是使⽤proxy。当mode为proxy时,只有缓存⽅法在外部被调⽤的时候Spring Cache才会发⽣作⽤,这也就意味着如果⼀个缓存⽅法在其声明对象内部被调⽤时Spring Cache是不会发⽣作⽤的。⽽mode为aspectj时就不会有这种问题。另外使⽤proxy时,只有public⽅法上的@Cacheable等标注才会起作⽤,如果需要⾮public⽅法上的⽅法也可以使⽤Spring Cache时把mode设置为aspectj。
此外,还可以指定⼀个proxy-target-class属性,表⽰是否要代理class,默认为false。我们前⾯提到的@Cacheable、@cacheEvict等也可以标注在接⼝上,这对于基于接⼝的代理来说是没有什么问题的,但是需要注意的是当我们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进⾏操作的,定义在接⼝上的@Cacheable等Cache注解不会被识别到,那对应的Spring Cache也不会起作⽤了。
需要注意的是只会去寻定义在同⼀个ApplicationContext下的@Cacheable等缓存注解。
2.1.2 基于XML配置
除了使⽤注解来声明对Cache的⽀持外,Spring还⽀持使⽤XML来声明对Cache的⽀持。这主要是通过类似于aop:advice的cache:advice 来进⾏的。在cache命名空间下定义了⼀个cache:advice元素⽤来定义⼀个对于Cache的advice。其需要指定⼀个cache-manager属性,默认为cacheManager。cache:
advice下⾯可以指定多个cache:caching元素,其有点类似于使⽤注解时的@Caching注解。
cache:caching元素下⼜可以指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们类似于使⽤注解时的
@Cacheable、@CachePut和@CacheEvict。下⾯来看⼀个⽰例:
上⾯配置定义了⼀个名为cacheAdvice的cache:advice,其中指定了将缓存findById⽅法和find⽅法到名为users的缓存中。这⾥的⽅法还可以使⽤通配符“*”,⽐如“find*”表⽰任何以“find”开始的⽅法。
有了cache:advice之后,我们还需要引⼊aop命名空间,然后通过aop:config指定定义好的cacheAdvice要应⽤在哪些pointcut上。如:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论