`
dylan0514sina.cn
  • 浏览: 94915 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

方法缓存

 
阅读更多
介绍
  spring3.1之后提供了方法的缓存支持,透明的将缓存添加到应用中。这种缓存架构类似事务架构,提供了不同的缓存方案。

理解缓存架构
  缓存架构的核心在于缓存Java方法,减少方法执行次数。就是说当目标方法执行时,架构会检查指定参数的方便是否已经被执行过,如果没有则执行,并缓存结果返回;否则直接返回缓存结果并不执行方法。当然这种情况只针对方法执行结果结果不会变。

  缓存和缓冲区 
   缓冲区用于在读写操作之间开放的临时存储区,操作快的一方必须等待慢的一方因此影响性能。缓存区通过开放大的比较合适的数据块而不是单字节,以缓解这种性能损失,特征是单条记录读写只有一次,而且至少有一方知道这个缓存区

   缓存是隐藏的当缓存行为发生时都不知道缓存的存在,允许同一份数据被读多次来提高性能

利用缓存架构主要关注两个方面
  声明缓存-标志要缓存的方法及策略
  缓存配置-后端缓存 数据读和写的地方
我们只需要关注缓存逻辑行为而不是真正读写的地方。spring提供了两种开箱即用的缓存配置实现--JDK java.util.concurrent.ConcurrentMap和 EhCache,当然也支持其他插拔式的缓存提供者。

基于注解的声明式缓存
  架构提供了两种注解 @Cacheable 和 @CacheEvict,允许方法触发缓存存储和缓存剔除,接下来了解每种注解

@Cacheable
  用于声明需要缓存的方法。方法的结果被存储到缓存当下次相同参数的方法调用时返回缓存的结果,而不需要调用目标方法,注解指定了方法的缓存名字:
  @Cacheable("books")
public Book findBook(ISBN isbn) {...}

在上面的片段中,books是和方法findBook关联的缓存名。每次方法调用时会检查这个缓存,看是否方法已经执行过而不必重新执行。在大多数情况下仅有一个缓存,但是也支持多个名指定多个缓存,这种情况下方法执行之前会依次检查这些缓存,如果命中则返回关联的结果。不管方法有没有执行,这几个缓存的数据将保持一致。
@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}


默认键值生成策略
缓存本质上由键值对实现,有必要为缓存的方法设计一个合理的键值以便访问缓存,架构提供了一个简单的KeyGenerator实现:
  • null值返回常数53
  • 没有参数,则返回0;
  • 只有一个参数,则返回参数本身
  • 多个参数,则计算所有参数的hash值并返回

一般情况下,只要hashcode()方法能反应键值就行。在分布式环境下如果参数实现remote接口,规定hashcode的实现中指定的是同一台服务器地址,而不同服务器上的相同方法hashcode必然不一样因此要作调整。不管在哪种环境,对这种算法来讲hashcode不可丢失。而且在同一jvm中,相同的hashcode能被不同的对象复用。

可以通过实现org.springframework.cache.KeyGenerator接口,提供不同的算法实现,配置如下
 <cache:annotation-driven key-generator="customGenerator"/>

一旦配置好,将被没指定key的方法使用。

指定key

尽管缓存是通用的,但目标方法签名可能是各式各样的,不能简单的映射为缓存键值。就是当方法有多个参数时,我们要选择合适的参数作为键值(部分)
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed

咋一看,虽然两个boolean参数影响数找到的结果,但对缓存来说没什么用,也有可能只有一个有用?
@Cacheable允许指定key是如何生成的,架构使用Spring表达式语言选择感兴趣的参数并进行计算,不需要实现其他接口。这是一种推荐的配置以覆盖默认的键值生成策略。
以下是 SpEL声明的例子
Cacheable(value="books", key="#isbn"
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)


@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)

以上片段中,指定,方法参数,参数属性,和任意静态方法。

缓存条件

有时候方法可能不适合总是缓存,例如依赖方法参数。架构提供了这样的功能,通过conditional 属性SpEL计算值,如果返回true,则方法被缓存,否则不缓存。下面的方法在参数name长度小于15时才缓存
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)

除了conditional,unless用于在方法执行完成之后根据条件是否设置结果到缓存中:
@Cacheable(value="book", condition="#name.length < 32", unless="#result.hardback")
public Book findBook(String name)

以上片段中,值缓存硬抄本而不是软抄本。

缓存SpEL的上下文
SpEL表达式计算需要专门的上下文,下面的表格中变量可以在key,conditional,unless中使用
name Location Description Example
methodName root object The name of the method being invoked #root.methodName
method root object The method being invoked #root.method.name
target root object The target object being invoked #root.target
targetClass root object The class of the target being invoked #root.targetClass
args root object The arguments (as array) used for invoking the target #root.args[0]
caches root object Collection of caches against which the current method is executed #root.caches[0].name
argument name evaluation context Name of any of the method argument. If for some reason the names are not available (ex: no debug information), the argument names are also available under the a<#arg> where #arg stands for the argument index (starting from 0). iban or a0 (one can also use p0 or p<#arg> notation as an alias).
result evaluation context The result of the method call (the value to be cached). Only available in 'unless' expressions and 'cache evict' expression (when beforeInvocation is false). #result


@CachePut
不干扰方法执行,只缓存方法结果。与@Cacheable有相同属性,显然不是用来做方法优化的
当和@Cacheable一起使用时,后者使用@Cacheable而阻止方法执行,前者强迫方法执行以更新缓存。这种情况会导致混乱的结果,除非在这两者的使用条件上相反,即任何时候值有一个是可用的。

@CacheEvict
用于剔除缓存中的数据,与@Cacheable作用相反。提供了一个allEntries属性,当allEntries=true时将清除指定名称缓存中的所有数据,此时架构将忽略key值。
beforeInvocation 用于指定剔除行为是方法执行之前还是之后,beforeInvocation =false和其他注解有相同的语义,在方法执行之后进行动作。如果方法因为缓存没有执行或执行抛出异常,则不会进行剔除动作;beforeInvocation =true指定在方法执行之前执行剔除动作,当不必考虑方法执行结果时非常有效。
可以对返回void方法进行@CacheEvict,而@Cacheable和@CachePut必须有返回值,即使为null

@Caching
当要使用同一种注解比如@CacheEvict 或 @CachePut,因为对不同的缓存区可能执行条件不一,@Caching 允许在一个方法上使用多个@Cacheable, @CachePut 和@CacheEvict:
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") })
public Book importBooks(String deposit, Date date)


激活缓存注解
声明完方法注解之后,需要激活注解也就是在方法调用时使用注解行为,使用
@EnableCaching:
@Configuration
@EnableCaching
public class AppConfig {

}

基于xml激活方式,只会在同一applicationContext中查找,不会找到父applicationContext:
<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-managerCachingConfigurer cacheManager Name of cache manager to use. Only required if the name of the cache manager is not cacheManager, as in the example above.
mode modeproxy The default mode "proxy" processes annotated beans to be proxied using Spring's AOP framework (following proxy semantics, as discussed above, applying to method calls coming in through the proxy only). The alternative mode "aspectj" instead weaves the affected classes with Spring's AspectJ caching aspect, modifying the target class byte code to apply to any kind of method call. AspectJ weaving requires spring-aspects.jar in the classpath as well as load-time weaving (or compile-time weaving) enabled.
proxy-target-class proxyTargetClass falseApplies to proxy mode only. Controls what type of caching proxies are created for classes annotated with the @Cacheable or @CacheEvict annotations. If the proxy-target-class attribute is set to true, then class-based proxies are created. If proxy-target-class is false or if the attribute is omitted, then standard JDK interface-based proxies are created.
order order Ordered.LOWEST_PRECEDENCEDefines the order of the cache advice that is applied to beans annotated with @Cacheable or @CacheEvict. (For more information about the rules related to ordering of AOP advice, see the section called “Advice ordering”.) No specified ordering means that the AOP subsystem determines the order of the advice.


代理注意事项  
   当使用代理时@Cache*注解只适用于public方法,在proctected,package,private方法上使用时不会报错也不会有任何行为,可以使用AspectJ。
   当在接口上使用@Cache*时,对proxy-target-class="true"即cglib代理或AspectJ是不可见的,是被忽略的。应在类上使用注解。
   在代理模式下,只有通过代理的方法才会被拦截使用注解;相反如果在一个方法内调用本对象的另一方法,则该方法“this”不是代理对象,则不被拦截。使用AspectJ mode。

自定义缓存注解 
   缓存架构支持自定义注解,当要使用特定于业务的特定配置时,需要自定义注解@Cacheable和@CacheEvict对缓存结构来讲都是原生注解,可以利用它们构建。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(value=“books”, key="#isbn")
public @interface 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)


基于xml声明注解
对于注解厌恶者来说,使用xml是个不错的选择
<cache:advice id="cacheAdviceInterface" cache-manager="cacheManager">
		<cache:definitions cache="default">
			<cache:cacheable method="cache"/>
			<cache:cacheable method="conditional" condition="#classField == 3"/>
			<cache:cacheable method="key" key="#p0"/>
			<cache:cacheable method="nam*" key="#root.methodName"/>
			<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
			<cache:cacheable method="nullValue" cache="default"/>
		</cache:definitions>
		<cache:definitions>
			<cache:cache-evict method="invalidate" cache="default"/>
			<cache:cache-evict method="evict" key="#p0" cache="default"/>
		</cache:definitions>
		<cache:caching cache="" condition="" key="" method="">
			<cache:cache-evict />
			<cache:cacheable />
			<cache:cache-put />
		</cache:caching>
	</cache:advice>

我们使用过spring事务配置,缓存配置使用同样的方式
<!-- the service we want to make cacheable -->
<bean id="bookService" class="x.y.service.DefaultBookService"/>

<!-- cache definitions -->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
    <cache:caching cache="books">
        <cache:cacheable method="findBook" key="#isbn"/>
        <cache:cache-evict method="loadBooks" all-entries="true"/>
    </cache:caching>
</cache:advice>

<!-- apply the cacheable behavior to all BookService interfaces -->
<aop:config>
    <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.BookService.*(..))"/>
</aop:config>
...
// cache manager definition omitted


配置CacheManager
spring内置了JDK ConcurrentMap 和ehcache库两种实现
基于JDK ConcurrentMap实现
<!-- 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>

以上片段创建了两个缓存区default和books,就是@cache*中的名字。只适合于一般使用:测试、简单应用。不提供持久化和剔除策略。

基于ehcache实现
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>

<!-- EhCache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>


基于GemFire实现
GemFire 是面向内存的以磁盘为后端的有弹性、可扩展性的缓存,是spring团队开发的。

处理无后端存储的缓存
有时转换环境或做测试时,可能有些@Cache*没指定名字或指定的不存在。一般来说会报错,这种情况下,比起移除注解,更加优雅的方式就是指定一个假的缓存,什么也不做--就是说强制方法每次都执行
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers"><list>
        <ref bean="jdkCache"/>
        <ref bean="gemfireCache"/>
    </list></property>
    <property name="fallbackToNoOpCache" value="true"/>
</bean>


内嵌第三方缓存
有很多缓存产品被用来做后端存储器,为了使用它们我们必须自己实现cacheManager和cache因为每种产品没有同一的标准,spring提供了AbstractCacheManager模版可供实现。
4
3
分享到:
评论
6 楼 dylan0514sina.cn 2013-07-12  
youjianbo_han_87 写道
dylan0514sina.cn 写道
youjianbo_han_87 写道
缓存方法有意义吗,方法+调用从缓存中取内容的方法 换成 方法+从缓存中直接取内容。换汤不换药啊。

缓存方法的前提是主要是目标方法调用结果不变且比较耗资源比如走网络查询数据字典,或走IO加载服务器下的文件时;与多加个调度器比,孰轻孰重?

你说的这些消耗资源的操作,spring是不是也要在内存里面先缓存起来?而我用缓存,本来就是为了缓存这些耗资源和IO的东东啊。

在其他的地方加缓存没什么不可以的,只是spring提供了缓存数据在方法调用这个时机作了统一管理。这与我们也可以自定义缓存 不冲突
5 楼 youjianbo_han_87 2013-07-12  
dylan0514sina.cn 写道
youjianbo_han_87 写道
缓存方法有意义吗,方法+调用从缓存中取内容的方法 换成 方法+从缓存中直接取内容。换汤不换药啊。

缓存方法的前提是主要是目标方法调用结果不变且比较耗资源比如走网络查询数据字典,或走IO加载服务器下的文件时;与多加个调度器比,孰轻孰重?

你说的这些消耗资源的操作,spring是不是也要在内存里面先缓存起来?而我用缓存,本来就是为了缓存这些耗资源和IO的东东啊。
4 楼 dylan0514sina.cn 2013-07-11  
youjianbo_han_87 写道
缓存方法有意义吗,方法+调用从缓存中取内容的方法 换成 方法+从缓存中直接取内容。换汤不换药啊。

缓存方法的前提是主要是目标方法调用结果不变且比较耗资源比如走网络查询数据字典,或走IO加载服务器下的文件时;与多加个调度器比,孰轻孰重?
3 楼 youjianbo_han_87 2013-07-11  
缓存方法有意义吗,方法+调用从缓存中取内容的方法 换成 方法+从缓存中直接取内容。换汤不换药啊。
2 楼 dylan0514sina.cn 2013-07-11  
Shen.Yiyang 写道
剔除策略只有方法执行的时候指定key或者所有entity吗? 有自定义的剔除接口吗?

没有
1 楼 Shen.Yiyang 2013-07-11  
剔除策略只有方法执行的时候指定key或者所有entity吗? 有自定义的剔除接口吗?

相关推荐

    如何解决缓存问题及如何设置缓存

    缓存问题解决方案和设置缓存方法 缓存是Web开发中一个常见的问题,它可以极大地影响网站的性能和用户体验。因此,解决缓存问题和设置缓存是非常重要的。在这篇文章中,我们将讨论如何解决缓存问题和设置缓存。 ...

    asp.net缓存sql数据库

    2. **方法级别的缓存**:对于特定的业务逻辑方法,可以使用`HttpRuntime.Cache`对象来手动添加和管理缓存。例如,获取数据库数据后,将其存储到缓存中,并关联一个键值,下次请求时先检查缓存是否存在该键值对应的...

    etCache 是一个基于 Java 的缓存系统封装,提供统一的 API 和注解来简化缓存的使用.rar

    JetCache是一个基于java的缓存系统封装,提供统一的API和注解简化...通过注解实现声明式的方法缓存 通过注解创建并配置Cache实例 针对所有Cache实例和方法缓存的自动统计 Key的生成策略和value的序列化策略是可以配置的

    JetCache是​​一个Java缓存框架 .zip

    使用带有 TTL(生存时间)和两级缓存支持的注释进行声明式方法缓存Cache使用缓存管理器创建并配置实例自动收集Cache实例和方法级缓存的访问统计信息可以自定义key的生成和value的序列化策略支持的缓存键转换器// ...

    基于Redis的Java Method缓存包.zip

    3. **方法缓存实现** - **注解驱动**:可以定义一个自定义注解,如`@Cacheable`,标记在需要缓存的方法上。注解中可以包含缓存的key生成策略、过期时间等信息。 - **AOP(面向切面编程)**:使用Spring的AOP,通过...

    深踩Android Studio 缓存的坑及解决方法

    深踩 Android Studio 缓存的坑及解决方法 Android Studio 是 Android 应用开发的主要 IDE,但是在使用过程中,我们经常会遇到缓存的问题。缓存的问题会导致项目依赖项无法更新,从而影响项目的开发和编译。在这篇...

    面试中缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级怎么解答?.docx

    另一种简单方法是将空结果也缓存,但设置较短的过期时间。 3. **缓存预热** 缓存预热是在系统上线前或重启后,提前加载一部分常用数据到缓存中,确保服务启动后能快速响应用户请求。这可以通过手动操作、自动化...

    基于模版方法的简单缓存功功能

    “基于模版方法的简单缓存功能”这一标题暗示了我们将讨论一个设计模式的应用,即模板方法模式,以及它如何被用来实现一个缓存系统。缓存是一种提升应用程序性能的技术,通过存储频繁访问的数据,减少对原始数据源的...

    阿里开源的缓存框架JetCache.pdf

    2. 通过注解实现声明式的方法缓存 3. 支持TTL和两级缓存 4. 通过注解创建并配置Cache实例 5. 针对所有Cache实例和方法缓存的自动统计 6. Key的生成策略和Value的序列化策略是可以配置的 7. 分布式缓存自动刷新 8. ...

    让html页面不缓存js的实现方法

    不缓存JS的方法其实挺简单,CSS在某种条件下也可以如此使用; 先让大家了解下不缓存的一个简单的原理: 当浏览不同Url时,浏览器会自动将当前访问的地址进行一次缓存;而第二次访问时着调用缓存下来的页面,从而达到...

    Spring 与Ehcache实现基于方法的缓存

    本篇文章将详细探讨如何在Spring框架中集成并实现基于方法的缓存机制,利用Ehcache来优化数据访问。 首先,我们需要理解Spring的AOP概念,AOP允许我们定义横切关注点,如日志、事务管理或,正如在这个案例中,缓存...

    .net c# memcached缓存获取所有缓存键的方法步骤.docx

    .NET C# Memcached 缓存获取所有缓存键的方法步骤 在软件开发中,缓存机制是提高系统性能和减少数据库访问频次的重要手段之一。Memcached 是一种流行的分布式缓存系统,广泛应用于各种 Web 应用程序中。.NET 平台下...

    spring简单的缓存

    当方法被调用时,其结果会被存储在指定的缓存中,下次调用时,如果缓存中有该结果,将直接返回,不再执行方法体。 - `@CacheEvict`:用于清除缓存中的数据。可以在方法执行前后,或者根据方法的返回值或异常情况...

    iBATIS缓存的使用方法

    ### iBATIS缓存的使用方法 在数据库访问框架iBATIS中,缓存机制是一项重要的功能,它能够显著提高应用程序的性能。本文将详细介绍iBATIS中的缓存使用方法,包括缓存模型的配置、不同类型的缓存控制器以及如何在SQL...

    缓存的处理方法

    在.NET中,可以使用`Cache.Insert`方法添加带有依赖项的缓存项,或者使用`Cache.Add`方法在不存在的情况下添加缓存项。 为了更好地管理缓存,我们需要考虑缓存过期策略。这可以通过设置绝对过期时间、相对过期时间...

    asp.net缓存 缓存

    在代码中,我们可以使用`Cache.Insert()` 方法添加缓存项,使用`Cache.Remove()` 删除缓存,或者通过`Cache.Get()` 获取缓存数据。 总之,ASP.NET缓存是提高应用程序性能的有效手段,但也需要谨慎使用,以确保数据...

    C++数据结构与算法之双缓存队列实现方法详解

    C++数据结构与算法之双缓存队列实现方法详解 本文主要介绍了C++数据结构与算法之双缓存队列实现方法,结合实例形式分析了双缓存队列的原理、实现方法与相关注意事项。 知识点一:双缓存队列的定义 双缓存队列是一...

    缓存、缓存算法和缓存框架简介.docx

    5. **随机替换(Random)**:简单地随机选择一个条目进行替换,这种方法简单但可能不高效。 缓存框架的选择通常是基于特定的业务需求和技术栈。在 Java 世界中,常见的缓存框架有: 1. **Ehcache**:一个广泛使用...

    处理文件缓存的方法

    标题"处理文件缓存的方法"提示我们关注的重点是管理和维护这些缓存。在实际应用中,这通常涉及到两个关键步骤:计算文件夹大小和清除缓存。 计算文件夹大小是一项基础但重要的任务,有助于了解缓存占用的存储空间。...

    清除Flash缓存和各种浏览器缓存的方法.docx

    清除Flash缓存和各种浏览器缓存的方法 清除Flash缓存和各种浏览器缓存的方法是计算机用户常用的技术技能,以解决浏览器缓存引发的问题,如卡机、进不去、游戏滞后等问题。下面将详细介绍清除Flash缓存和各种浏览器...

Global site tag (gtag.js) - Google Analytics