方法缓存

[size=x-large]介绍[/size]
spring3.1之后提供了方法的缓存支持,透明的将缓存添加到应用中。这种缓存架构类似事务架构,提供了不同的缓存方案。

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

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

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

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

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

[size=large]@Cacheable[/size]
用于声明需要缓存的方法。方法的结果被存储到缓存当下次相同参数的方法调用时返回缓存的结果,而不需要调用目标方法,注解指定了方法的缓存名字:

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

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

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


[size=medium]默认键值生成策略[/size]
缓存本质上由键值对实现,有必要为缓存的方法设计一个合理的键值以便访问缓存,架构提供了一个简单的KeyGenerator实现:
[list]
[*]null值返回常数53
[*]没有参数,则返回0;
[*]只有一个参数,则返回参数本身
[*]多个参数,则计算所有参数的hash值并返回
[/list]
一般情况下,只要hashcode()方法能反应键值就行。在分布式环境下如果参数实现remote接口,规定hashcode的实现中指定的是同一台服务器地址,而不同服务器上的相同方法hashcode必然不一样因此要作调整。不管在哪种环境,对这种算法来讲hashcode不可丢失。而且在同一jvm中,相同的hashcode能被不同的对象复用。

可以通过实现org.springframework.cache.KeyGenerator接口,提供不同的算法实现,配置如下

<cache:annotation-driven key-generator="customGenerator"/>

一旦配置好,将被没指定key的方法使用。
[size=medium]
指定key[/size]
尽管缓存是通用的,但目标方法签名可能是各式各样的,不能简单的映射为缓存键值。就是当方法有多个参数时,我们要选择合适的参数作为键值(部分)

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

以上片段中,指定,方法参数,参数属性,和任意静态方法。
[size=medium]
缓存条件[/size]
有时候方法可能不适合总是缓存,例如依赖方法参数。架构提供了这样的功能,通过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)

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

[size=medium]缓存SpEL的上下文[/size]
SpEL表达式计算需要专门的上下文,下面的表格中变量可以在key,conditional,unless中使用
[table]
||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

[/table]

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

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

[size=large]@Caching[/size]
当要使用同一种注解比如@CacheEvict 或 @CachePut,因为对不同的缓存区可能执行条件不一,@Caching 允许在一个方法上使用多个@Cacheable, @CachePut 和@CacheEvict:

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


[size=large]激活缓存注解[/size]
声明完方法注解之后,需要激活注解也就是在方法调用时使用注解行为,使用
@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>

激活属性设置
[table]
|| cache-manager|CachingConfigurer |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| mode|proxy |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| false|Applies 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_PRECEDENCE|Defines 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.
[/table]

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

[size=x-large]自定义缓存注解[/size]
缓存架构支持自定义注解,当要使用特定于业务的特定配置时,需要自定义注解@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)


[size=x-large]基于xml声明注解[/size]
对于注解厌恶者来说,使用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


[size=x-large]配置CacheManager [/size]
spring内置了JDK ConcurrentMap 和ehcache库两种实现
[size=large]基于JDK ConcurrentMap实现[/size]

<!-- 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*中的名字。只适合于一般使用:测试、简单应用。不提供持久化和剔除策略。

[size=large]基于ehcache实现[/size]

<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"/>


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

[size=large]处理无后端存储的缓存[/size]
有时转换环境或做测试时,可能有些@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>


[size=large]内嵌第三方缓存[/size]
有很多缓存产品被用来做后端存储器,为了使用它们我们必须自己实现cacheManager和cache因为每种产品没有同一的标准,spring提供了AbstractCacheManager模版可供实现。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值