1.启用缓存
Spring对缓存的支持有两种方式:
1.注解驱动的缓存
2.XML声明的缓存
使用Spring的缓存抽象时,最为通用的方式就是在方法上添加@Cacheable和@CacheEvict注解。
在本例中,声明了ConcurrentMapCacheManager,这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为其缓存存储。
如果使用Java配置的话,那么可以在其中的一个配置类上添加@EnableCaching,这样的话就能启用注解驱动的缓存。
如果以XML的方式配置应用的话,那么可以使用Spring cache命名空间中的
在本质上,@EnableCaching和
配置缓存管理器
Spring 3.1内置了五个缓存管理器实现,如下所示:
1.SimpleCacheManager
2.NoOpCacheManager
3.ConcurrentMapCacheManager
4.CompositeCacheManager
5.EhCacheCacheManager
Spring 3.2引入了另外一个缓存管理器,这个管理器可以用在基于JCache(JSR-107)的缓存提供商之中。除了核心的Spring框架,Spring Data又提供了两个缓存管理器:
1.RedisCacheManager(来自于Spring Data Redis项目)
2.GemfireCacheManager(来自于Spring Data GemFire项目)
使用Ehcache缓存
cacheManager()方法创建了一个EhCacheCacheManager的实例,这是通过传入Ehcache CacheManager实例实现的。需要明确的是,EhCache的CacheManager要被注入到Spring的EhCacheCacheManager(Spring CacheManager的实现)之中。
我们需要使用EhCache的CacheManager来进行注入,所以必须也要声明一个CacheManager bean。为了对其进行简化,Spring提供了EhCacheManager-FactoryBean来生成EhCache的CacheManager。方法ehcache()会创建并返回一个EhCacheManagerFactoryBean实例。因为它是一个工厂bean(也就是说,它实现了Spring的FactoryBean接口),所以注册在Spring应用上下文中的并不是EhCacheManagerFactoryBean的实例,而是CacheManager的一个实例,因此适合注入到EhCacheCacheManager之中。
除了在Spring中配置的bean,还需要有针对EhCache的配置。EhCache为XML定义了自己的配置模式,我们需要在一个XML文件中配置缓存,该文件需要符合EhCache所定义的模式。在创建EhCacheManagerFactoryBean的过程中,需要告诉它EhCache配置文件在什么地方。在这里通过调用setConfigLocation()方法,传入ClassPath-Resource,用来指明EhCache XML配置文件相对于根类路径(classpath)的位置。
ehcache.xml文件的内容,不同的应用之间会有所差别,但是至少需要声明一个最小的缓存。例如,如下的EhCache配置声明一个名为spittleCache的缓存,它最大的堆存储为50MB,存活时间为100秒。
使用redis缓存
Spring Data Redis提供了RedisCacheManager,这是CacheManager的一个实现。RedisCacheManager会与一个Redis服务器协作,并通过RedisTemplate将缓存条目存储到Redis中。
配置如下所示:
使用多个缓存管理器
使用超过一个缓存管理器的话,那么可以使用Spring的CompositeCacheManager。
CompositeCacheManager要通过一个或更多的缓存管理器来进行配置,它会迭代这些缓存管理器,以查找之前所缓存的值。如:
当查找缓存条目时,CompositeCacheManager首先会从JCacheCacheManager开始检查JCache实现,然后通过EhCacheCacheManager检查Ehcache,最后会使用RedisCacheManager来检查Redis,完成缓存条目的查找。
2.为方法添加注解 以支持缓存
Spring的缓存抽象在很大程度上是围绕切面构建的。在Spring中启用缓存时,会创建一个切面,它触发一个或更多的Spring的缓存注解。
下面的表列出了Spring所提供的缓存注解。表中的所有注解都能运用在方法或类上。当将其放在单个方法上时,注解所描述的缓存行为只会运用到这个方法上。如果注解放在类级别的话,那么缓存行为就会应用到这个类的所有方法上。
填充缓存
@Cacheable和@CachePut注解都可以填充缓存,但是它们的工作方式略有差异。
@Cacheable首先在缓存中查找条目,如果找到了匹配的条目,那么就不会对方法进行调用了。如果没有找到匹配的条目,方法会被调用并且返回值要放到缓存之中。而@CachePut并不会在缓存中检查匹配的值,目标方法总是会被调用,并将返回值添加到缓存之中。
使用方式:
当findOne()被调用时,缓存切面会拦截调用并在缓存中查找之前以名spittleCache存储的返回值。缓存的key是传递到findOne()方法中的id参数。如果按照这个key能够找到值的话,就会返回找到的值,方法不会再被调用。如果没有找到值的话,那么就会调用这个方法,并将返回值放到缓存之中,为下一次调用findOne()方法做好准备。
把@Cacheable注解放到接口上,@Cacheable会被所有实现类继承。
@Cacheable会条件性地触发对方法的调用,这取决于缓存中是不是已经有了所需要的值,对于所注解的方法,@CachePut采用了一种更为直接的流程。带有@CachePut注解的方法始终都会被调用,而且它的返回值也会放到缓存中。这提供一种很便利的机制,能够让我们在请求之前预先加载缓存。
当save()方法被调用时,它首先会做所有必要的事情来保存Spittle,然后返回的Spittle会被放到spittleCache缓存中。
自定义缓存key
@Cacheable和@CachePut都有一个名为key属性,这个属性能够替换默认的key,它是通过一个SpEL表达式计算得到的。任意的SpEL表达式都是可行的,但是更常见的场景是所定义的表达式与存储在缓存中的值有关,据此计算得到key。
定义缓存规则的SpEL扩展
对于save()方法来说,我们需要的键是所返回Spittle对象的id属性。表达式#result能够得到返回的Spittle。借助这个对象,我们可以通过将key属性设置为#result.id来引用id属性:
@Cacheable和@CachePut提供了两个属性用以实现条件化缓存:unless和condition,这两个属性都接受一个SpEL表达式。如果unless属性的SpEL表达式计算结果为true,那么缓存方法返回的数据就不会放到缓存中。与之类似,如果condition属性的SpEL表达式计算结果为false,那么对于这个方法缓存就会被禁用.
unless属性只能阻止将对象放进缓存,但是在这个方法调用的时候,依然会去缓存中进行查找,如果找到了匹配的值,就会返回找到的值。与之不同,如果condition的表达式计算结果为false,那么在这个方法调用的过程中,缓存是被禁用的。就是说,不会去缓存进行查找,同时返回值也不会放进缓存中。
假设对于message属性包含“NoCache”的Spittle对象,我们不想对其进行缓存。为了阻止这样的Spittle对象被缓存起来,可以这样设置unless属性:
为了要对ID小于10的Spittle关闭缓存,可以在@Cacheable上使用condition属性
如果findOne()调用时,参数值小于10,那么将不会在缓存中进行查找,返回的Spittle也不会放进缓存中,就像这个方法没有添加@Cacheable注解一样。
移除缓存条目
如果带有@CacheEvict注解的方法被调用的话,那么会有一个或更多的条目会在缓存中移除。
当remove()调用时,会从缓存中删除一个条目。被删除条目的key与传递进来的spittleId参数的值相等。
@CacheEvict注解的属性,指定了哪些缓存条目应该被移除掉
3.使用XML声明缓存
需要包含的命名空间
使用:
在本例中,只包含了一个
1.cache:指明要存储和获取值的缓存;
2·condition:SpEL表达式,如果计算得到的值为false,将会为这个方法禁用缓存;
3.key:SpEL表达式,用来得到缓存的key(默认为方法的参数);
4.method:要缓存的方法名。
除此之外,
1.all-entries:如果是true的话,缓存中所有的条目都会被移除掉。如果是false的话,只有匹配key的条目才会被移除掉。
2.before-invocation:如果是true的话,缓存条目将会在方法调用之前被移除掉。如果是false的话,方法调用之后才会移除缓存。
all-entries和before-invocation的默认值都是false。这意味着在使用
参考《Spring实战》