`

Spring 3.1 配置cache详解

 
阅读更多
spring的cache方案需要相同参数调用同一个方法在数据一致的情况下返回结果也应该是一致的。

使用spring cache只需要完成两部分:

·缓存声明:在方法上加上相应缓存注解和相应策略

·configuration:定义缓存位置和具体保存策略

(spring cache并不是完全由spring提供,和transaction一样,只是对第三方框架进行上层封装)。

第一分部:缓存声明

1、基于注解的缓存声明:

我们只需要学习四个注解:@Cacheable、@CachePut 、 @CacheEvict 和@Caching

@Cacheable:

正如其名字,@Cacheable用于添加在需高速缓存的方法上。这些方法默认会以参数为主键把返回结果存储到高速缓存中,以便在随后的调用(使用相同的参数)方法,直接返回高速缓存中的值,不需要实际执行此方法。

@Cacheable注解有三个参数,value是必须的,还有key和condition。第一个参数,也就是value指明了缓存将被存到什么地方。


最简单的方式,只需要声明一个相关缓存策略的名称:
@Cacheable("books")
public Book findBook(ISBN isbn) {...}

也可以设置多个缓冲块,其中一个缓冲块命中即会返回,并会同步其他缓存块:

@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}


默认缓存主键:

缓存是采用键值的方式存储,所以每次调用都要将相应的参数转化成一个合适的高效缓存主键。
默认的主键生成策略:
·如果没有参数,返回0;
·如果存在一个参数,则返回该参数实例;
·如果不止一个参数,返回所有参数的哈希计算值。
也可以同时实现org.springframework.cache.KeyGenerator来定义自己特定的主键生成策略。

自定义缓存主键:

由于缓存块是通用的,所以不能简单的进行缓存主键声明,这样将导致生成的主键与业务不服或者与其他业务重复,如:

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

看上去应该并不是所有参数对于生成缓存主键都是有意义的。

任何存储在缓存中的数据为了高速访问都需要一个key。Spring默认使用被@Cacheable注解的方法的签名来作为key,当然你可以重写key,自定义key可以使用SpEL表达式。

像这种情况下,允许通过key属性来指定主键生成策略,且key支持使用SpEL:

@Cacheable(value="books", key="#isbn"
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);


注解中"#isbn"是一个SpEL表达式,他将使用findBook()方法中的isbn参数作为key。


@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);


@Cacheable(value="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);

上面的key可以简单的通过SpEL调用参数的属性。

缓存条件(何时缓存):

同样可以对condition参数通过SpEL表达式,当返回true时将被缓存,若返回false会执行方法。

如下,当名字长度小于32时才会缓存:

@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)


@CachePut:
用法类似于@Cacheable,但是用于存放数据声明(如更新数据),所以每次都会执行,将执行后的结果存入缓存。

所以不建议把@CachePut and @Cacheable放在同一方法上对于需要更新的数据我们应使用 @CachePut

@CacheEvict:
此注解对于去除无效的数据是非常重要的。@CacheEvict用于触发去除缓存中的数据。

除了和上面的注解用于标识缓存策略、主键和判断方式等外,又添加了allEntries属性,用于标识是否不仅只删除基于主键的数据:

@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch);

如上当需要清除一个“区域”的所有数据,而不是只清除一条数据,所以即使指定主键也是无用的。

除次之外还提供了一个beforeInvocation属性,用于表示是在执行前清除还是之后。这个属性是很有用的,比如当你在声明一个更新缓存方法之上(结合@Cacheable的场景)。

If the method does not execute (as it might be cached) or an exception is thrown, the eviction does not occur. 
The latter (beforeInvocation=true) causes the eviction to occur always, before the method is invoked - this is useful in cases where the eviction does not need  to be tied to the method outcome.
但是当无返回值(void)时,结合Cacheable将没有什么意义。

It is important to note that void methods can be used with @CacheEvict - as the methods act as triggers, the return values are ignored (as they don't interact with the cache) - this is not the case with @Cacheable which adds/update data into the cache and thus requires a result.


@Caching:
有时我们需要添加多个注解,可以通过此注解嵌套在一起。

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key =
"#p0") })
public Book importBooks(String deposit, Date date);


自定义注解:

有时缓存的一些注解配置是常用重复的,为了避免来回拷贝,你可以自定义自己的注解,如:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(value=“books”, key="#isbn")
public @interface SlowService {
}

然后你就可以通过你自定义的@SlowService注解替代下面的做法:

@Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

替代方式:

@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

(这样Spring就会自动识别,当然前提是你已经开启了缓存注解支持)

学习了,注解如何使用,那么如何通知Spring扫描相关注解呢?

缓存注解开启开关:
有时,你需要一个统一的开关进行控制缓存的开启和关闭。

只需要在你标注了@Configuration注解的类上添加@EnableCaching注解即可。
@Configuration
@EnableCaching
public class AppConfig {
}

或者通过XML方式配置使用的缓存:annotation-driven

<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.xsd  
http://www.springframework.org/schema/cache http://www.springframework.org/schema/  
cache/spring-cache.xsd">  
    <cache:annotation-driven />  
</beans>  


<cache:annotation-driven />用于指定了缓存“封装”策略,那么具体的“实现”策略如何指定呢?

第二部分:configuration

2、Configuring the cache storage:

使用过Spring的同学应该很清楚,通过IOC,spring让各个层面的实现易于替换,spring cache也是如此。

添加缓存注解声明后,我们需要指定一个缓存管理器--“明确”具体的数据保存策略。

下面我们列出了两种:通过ConcurrentMap和ehcache。

JDK ConcurrentMap-based Cache:


<!-- generic cache manager -->  
<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="books"/>  
        </set>  
    </property>  
</bean>  


上面的cache策略,简单的通过SimpleCacheManager来实现,可以用来测试或简单缓存(但是会非常高效,这种简单方式并没有指定超时等策略),配置中添加了两个缓存名称default、books。

Ehcache-based Cache:
ehcache的实现放在包org.springframework.cache.ehcache中,同样,也只需要非常简单的配置:

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cachemanager-  
ref="ehcache"/>  
<!-- Ehcache library setup -->  
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configlocation="  
ehcache.xml"/>  


第一部分扩充:基于XML的缓存

如果项目不允许或者不习惯使用注解,也可以像transaction一样,使用XML方式进行声明指定。



cache:advice配置中添加了对bookService的缓存策略,缓存策略名为books,对方法findBook进行了缓存,主键为#isbn。还包括清除缓存的声明。

aop:advisor通过AspectJ的方式进行了通知。

方法和transaction非常相似,可参考相关文章。

通过XML方式有一些好处:对已有代码没有任何侵入;便于统一指定范围内的类等。



第三部分:Hello World

第一步:添加缓存注解

// 添加缓存声明,demo1为缓存策略名称(我们会在XML中声明此策略)  
@Cacheable("demo1")  
@Override
public User get(Serializable entityId) {  
    return super.get(entityId);  
}  


第二步:打开缓存注解(我们采用的XML开启方式)
<cache:annotation-driven />

第三步:声明具体缓存策略

<cache:annotation-driven cache-manager="cacheManager" />  
  
<!-- generic cache manager -->  
<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="demo1" />  
        </set>  
    </property>  
</bean>  


整体配置代码
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	 xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
			http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
    <cache:annotation-driven cache-manager="cacheManager" />  
  
	<!-- generic cache manager -->  
	<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="gps" />  
	        </set>  
	    </property>  
	</bean>  
</beans>  



更新数据时,使缓存失效,下一次从数据库中获取

在Demo1基础上,更改如下即可:

@CacheEvict(value = "demo1", key = "#entity.userId")  
@Override  
public void update(Object entity) {  
    super.update(entity);  
}  


@CacheEvict的使用规则参照文章中相关解释。
试验步骤:打开列表页 -- > 修改数据--提交 -- > 刷新列表页数据已被更改 -- > 继续点击此数据修改连接(关闭只打开不修改),显示的数据为修改后的,证明@CacheEvict发挥了清除缓存的功能 -- > 然后直接SQL通过工具修改数据库该字段(一定要在修改后再次点击修改之后) -- > 再次点击列表页中此数据的修改连接,回显的数据非数据库实时数据,而是上次修改后的值,证明获取继续走了缓存。


将更新后的数据进行缓存,且更新前清除缓存

//不添加@CacheEvict,只添加@Cacheable,更新是无效的  
@CacheEvict(value = "demo1", key = "#entity.userId", beforeInvocation = true)  
@Cacheable(value = "demo1", key = "#entity.userId")  
@Override  
public User updateUser(User entity) {  
    // 更改了原有设计,返回了更改后的对象  
    super.update(entity);  
    return entity;  
}  


测试步骤:打开列表页 --> 修改一条数据 --> 打开数据库,验证更新生效,且从数据库中修改本条数据 --> 继续修改此条数据,回显数据为界面更改后的,不是数据库中实时数据(因为更新的主键和查询缓存主键相同)
也可以通过一个注解实现:

@CachePut(value = "demo1", key = "#entity.userId")  
@Override  
public User updateUser(User entity) {  
    // 更改了原有设计,返回了更改后的对象  
    super.update(entity);  
    return entity;  
}  


ehcache支持

我们接下来是介绍的Spring如何借助ehcache来对bean(dao、service、controller...)的调用结果进行缓存。(一般还有另外一种结合方案,如hibernate本身支持对ehcache的结合)
缓存注解无需变更和SimpleCacheManager一致,没有任何区别,所用其他缓存策略,只需要更改配置即可:

首先配置文件分两部分,spring指定ehcache和ehcache本身的缓存策略配置:

<cache:annotation-driven cache-manager="cacheManager"  
    key-generator="keyGenerator" />  
  
<!-- spring-cache:cache相关 -->  
<bean id="cacheManagerFactory"  
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"  
    p:configLocation="classpath:META-INF/ehcache.xml" />  
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"  
    p:cacheManager-ref="cacheManagerFactory" />  


ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<!--   
• timeToIdleSeconds – The maximum number of seconds an element can exist in the cache without being accessed. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime).  
• timeToLiveSeconds – The maximum number of seconds an element can exist in the cache regardless of use. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).  
• maxElementsOnDisk – The maximum sum total number of elements (cache entries) allowed for a distributed cache in all Terracotta clients. If this target is exceeded, eviction occurs to bring the count within the allowed target. The default value is 0, which means no eviction takes place (infinite size is allowed). Note that this value reflects storage allocated on the Terracotta Server Array. A setting of 0 means that no eviction of the cache's entries takes place on Terracotta Server Array, and consequently can cause the servers to run out of disk space.  
• eternal – If the cache–™s eternal flag is set, it overrides any finite TTI/TTL values that have been set.   
-->  
<ehcache>  
    <defaultCache maxElementsInMemory="10" eternal="false"  
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false">  
    </defaultCache>  
    <cache name="demo1" maxElementsInMemory="20000" eternal="false"  
        timeToIdleSeconds="3600" timeToLiveSeconds="1800" overflowToDisk="false" />  
  
    <!-- <terracottaConfig url="localhost:9510"/> -->  
</ehcache>    


注意:@Cacheable(cacheName="demo1") 中的name属性和ehcache.xml  中的<cache name="demo1"必须要一致。
加入相关的jar:

ehcache-core.2.4.7 不能用2.5否则会报错

配置的过程的出现转换的错误,解决方案链接:
http://zhouhaitao.iteye.com/blog/852225


参考:
http://www.oschina.net/question/82993_70254
http://jinnianshilongnian.iteye.com/blog/2001040
http://jinnianshilongnian.iteye.com/blog/2001040
  • 大小: 55.8 KB
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    Spring Cache的基本使用与实现原理详解

    Spring Cache 是Spring框架提供的一种缓存抽象,从Spring 3.1版本开始引入,目的是为了简化应用程序中的缓存管理,实现缓存透明化。通过在方法上添加特定注解,如@Cacheable、@CacheEvict等,可以轻松地启用缓存功能...

    spring-framework-reference.pdf

    - **Cache Abstraction**:Spring 3.1引入了缓存抽象,使得开发人员可以轻松地使用不同的缓存解决方案。 - **Bean Definition Profiles**:Spring 3.1支持定义Bean的配置文件,可以根据不同的环境选择不同的配置。 -...

    Spring与ehcache结合使用

    ##### 3.1 配置ehcache.xml文件 在开始之前,首先需要配置ehcache的XML文件。这是一个非常重要的步骤,因为它定义了缓存的规则和策略。下面是一个简单的示例配置: ```xml xsi:noNamespaceSchemaLocation=...

    Spring+EhCache缓存实例

    **Spring+EhCache缓存实例详解** 在现代的Java企业级应用中,缓存技术扮演着至关重要的角色,它能够显著提升系统性能,减少数据库负载。Spring框架与EhCache的结合,为开发者提供了一种高效、易用的缓存解决方案。...

    springboot知识点整理

    2 Spring Boot配置 18 2.1 配置文件 18 2.2 YML语法 19 2.3 YML配置文件值获取 21 2.4 properties配置文件乱码问题 24 2.5 @ConfigurationProperties与@Value的区别 25 2.6 配置@PropertySource、@ImportResource、@...

    Spring整合EhCache

    从 Spring 3.1 版本开始,Spring 框架正式引入了对 Cache 的支持,这种支持的方式和原理与 Spring 对事务管理的支持非常类似。通过这种方式,开发人员可以在不增加大量额外代码的情况下轻松地集成缓存技术,从而提高...

    Spring 缓存抽象示例详解

    Spring 缓存抽象是Spring框架从3.1版本开始引入的一种功能,旨在提供一种统一的方式来管理和集成各种缓存实现,如EhCache、Hazelcast、Infinispan和Redis等。这一抽象使得开发者能够在不关注具体缓存实现的情况下,...

    spring缓存代码详解

    从Spring 3.1开始,Spring引入了声明式的缓存管理API,类似于声明式事务管理。它提供了一个抽象接口,可以统一处理不同的缓存解决方案,使得代码的改动最小。 缓存的主要作用在于,当带有特定参数的方法首次被调用...

    JSF+HIbernate+Spring结合

    #### 三、Spring配置详解 Spring框架作为整个架构的核心,其配置尤为重要。 ##### 3.1 数据源配置 使用`BasicDataSource`或`DriverManagerDataSource`来配置数据源。例如: ```xml &lt;value&gt;...

    EHCache详解_技术文档

    **3.1 集成Spring** 1. **添加依赖**:除了EHCache核心库外,还需要添加Spring对EHCache的支持依赖。 ```xml &lt;groupId&gt;org.springframework &lt;artifactId&gt;spring-cache ${spring.version} ``` 2. **配置...

    spring指南

    ### Spring 指南知识点详解 #### I. Spring框架概览 ##### 1. Spring框架简介 - **依赖注入与控制反转(Dependency Injection and Inversion of Control)**: Spring框架的核心特性之一是支持依赖注入(DI),它...

    详解Spring整合Ehcache管理缓存

    # Spring 整合 Ehcache 管理缓存详解 Ehcache 是一个高性能、轻量级的缓存框架,适用于 Java 应用程序。它可以极大地提高数据访问速度,减轻数据库负载。Spring 框架则提供了对缓存功能的抽象,允许开发者选择不同...

    echache缓存注解说明文档

    【Spring Ecache缓存注解详解】 Spring框架从3.1版本开始引入了对缓存的支持,这使得在应用程序中实现缓存管理变得更加便捷。Spring Cache的设计理念与Spring的事务管理相似,它允许开发者在方法级别声明缓存,从而...

    Springboot整合redis使用技巧.docx

    ### Spring Boot 整合 Redis 使用技巧详解 #### 一、Spring Cache 概览 Spring 3.1 版本引入了基于注解的缓存支持,提供了高度灵活且易于使用的缓存抽象层。Spring Cache 不仅支持使用 SpEL(Spring Expression ...

    Ehcache分布式缓存与其在spring中的使用

    ##### 2.3 配置详解 `ehcache.xml`文件中包含了许多重要的配置参数: - `name`:缓存名称。 - `maxElementsInMemory`:缓存在内存中的最大元素数量。 - `eternal`:元素是否永久有效。 - `timeToIdleSeconds`:...

    spring-hadoop.pdf

    ### Spring与Hadoop集成知识点详解 #### 一、Spring与Hadoop集成概述 Spring与Hadoop集成是指在Spring框架中引入Hadoop的功能,利用Spring强大的依赖注入和面向切面编程能力来简化Hadoop应用程序的开发过程。通过...

    ehcache学习文档

    **3.1 Spring 集成** Spring 框架提供了内置的支持来集成 Ehcache。这使得在 Spring 应用中管理和使用缓存变得更加简单。主要步骤包括: - 添加 Ehcache 相关依赖到项目中。 - 配置 Spring 的缓存管理器,将其绑定...

    idea热加载.md

    ### IDEA 实现 Spring Boot 热加载详解 #### 一、引言 在软件开发过程中,尤其是前后端分离的应用场景下,频繁地修改代码并测试是常态。传统的做法是在每次修改完代码之后都需要手动重启应用服务器,这不仅耗时而且...

    MyBatis源码分析.pdf

    映射文件解析过程主要包括解析&lt;cache&gt;节点、&lt;cache-ref&gt;节点、节点等。 3.1 映射文件解析入口 映射文件解析入口是MyBatis的映射文件解析过程的入口点,负责将映射文件解析到MappedStatement对象中。MyBatis提供了...

Global site tag (gtag.js) - Google Analytics