Spring 对缓存的抽象
Cache vs. Buffer
A buffer is used traditionally as an intermediate temporary store for data between a fast and slow entity. As one party world have to wait for the other affecting performance, the buffer alleviates this by allowing entire blocks of data to move once rather than in small chunks. The data is written and read only once from the buffer. Furthermore, the buffers are visible to at least one party which is aware of it.
A cache on the other hand by definition is hidden and neither party is aware that caching occurs. It as well improves performance but does that by allowing the same data to be read multiple times in a fast fashion.
更多解释,可以参考维基百科https://en.wikipedia.org/wiki/Cache_(computing)#The_difference_between_buffer_and_cache。
本文讨论的 Spring 版本为 4.2,较早版本的不同会在文中提及,但不会重点描述。
一、Spring 中的缓存(cache)
接下来的篇幅中,我将使用 Spring 来代替 Spring Framework。
像 Spring 中提供的其他服务一样,Spring 为缓存也仅仅提供抽象,而不是具体的实现。这样,开发者不需要写具体的缓存逻辑但是需要提供实际的存储的实现。Spring 中缓存的抽象通过 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 这两个接口体现。
虽然 Spring 的缓存抽象本身不提供具体的存储实现,但是 Spring 之外有很多实现,如:Ehcache、Caffeine、guaua等。
注意,Spring 的 cache 抽象并没有针对多线程、多进行环境做特殊处理,这样的特性由具体的 cache 实现来处理。
如果处于一个多进程的环境,比如你的应用部署在多个节点上,你就必须处理同步问题,以免造成节点间数据不一致。
要使用 cache 抽象,开发者需要关注两个方面:
- 缓存声明:表明某个方法需要缓存,及缓存策略;
- 缓存配置:缓存数据保存在哪里,从哪里读取。
二、以基于注解的方式使用缓存
@Cacheable
@Cacheable 用于一个表示缓存该方法的返回值。后续对该方法的调用,可以不执行该方法,直接从缓存中返回结果。
@Cacheable("books")
public Book findBook(ISBN isbn) {}
上边的代码,findBook 方法关联的缓存名字为books。一个缓存也能有多个名字,比如下面的代码段。
@Cacheable("books", "isbns")
public Book findBook(ISBN isbn) {}
注意,在上面的代码示例中,如果在名为 isbns 的缓存中命中,而 books 中没有命中,那么会不执行方法,直接返回结果,并且 books 中也会加入在 isbns 中命中的数据。
1. 键
缓存本质上就是键值对,Spring 的缓存抽象基于以下算法通过 KeyGenerator 提供了生成:
- 如果方法没有参数,返回
SimpleKey.EMPTY; - 如果方法有且只有一个参数,返回这个参数;
- 如果方法有超过一个的参数,返回一个包含所有参数的
SimpleKey。
如果参数有自然键(nstural keys)并有效的实现了 hashCode() 和 equals() 方法,那么这种方式可以非常好的工作。否则,就需要改变策略。
4.0 版本之前的生成算法不同,需要了解更多的,请参考这里。
除了自动的生成策略,开发者还可以根据实际需要自定义缓存的键,主要实现方式是通过 SqEL。
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
除通过 key 属性自定义键,还可以通过 keyGenerator 来指定一个 KeyGenerator。
Spring 提供的缓存抽象不针对多线程环境多任何特殊处理,但可以通过 sync 属性来暗示缓存提供方(具体实现)在计算值的时候锁住缓存。也就是说,在进行计算的时候,只有一个线程工作,其他线程阻塞直到缓存更新。
@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
2. 条件化缓存
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")
public Book findBook(String name)
condition、unless 属性都可以用来实现条件化缓存。如果 unless 属性的表达式计算的结果为 true,那么缓存方法返回的数据就不会放到缓存中。如果 condition 属性的表达式计算的结果为 false,那么对于这个方法的缓存就会被禁用掉。
需要注意的是,unless 属性只能阻止将对象放进缓存,但是在这个方法调用的时候,依然会去缓存中查找。而如果 condition 属性的表达式计算结果为 false,那么直接禁用缓存(不会去缓存查找,返回值也不会放进缓存中)。
@CachePut
@CachePut 仅更新缓存,而不插手方法的执行。也就是说,方法总会执行,并把执行结果放进缓存中。它支持 @Cacheable 的所有选项。
需要注意,在同一个方法上同时使用 @CachePut、@Cacheable 将产生不确定的行为,千万不要这么做。
@CacheEvict
@CacheEvict 用于从缓存中删除数据,跟 @Cacheable 一样,@CacheEvict 需要指定一个或多个名字,允许自定义缓存决议、键决议,可以指定条件,除此之外,还可以通过 allEntries 来表示影响范围(单条数据,还是全部数据)。
@Caching
@Caching 用于在一个方法上组合多个 @Cacheable、@CachePut、@CacheEvict。
@CacheConfig
如果在一个类的所有方法的缓存上需要使用相同的缓存名字、自定义的 KeyGenerator、或自定义的 CacheManager,甚至是自定义的 CacheResolver,那么可以通过在类上使用 @CacheConfig 来完成。
在方法上定义会覆盖 @CacheConfig 的定义。
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
那么我们来总结一下,在三个级别上为每个缓存操作进行自定义的途径:
- 全局配置:
CacheManager、KeyGenerator。 - 类级别:
@CacheConfig - 操作级别,即在具体的方法上。
3. 启用基于注解的缓存
虽然具体注解介绍完了,但是像 Spring 中的其他组件一样,需要启用才能真正发挥作用。当然还有很多配置项,这里我只介绍启用方式。
通过基于 Java 注解的方式启用:
@Configuration
@EnableCaching
public class CacheConfig {
}
通过 XML 配置启用:
<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>
到这里,本文的主要的内容就结束了。如果你想了解使用自定义注解,集成具体的缓存实现,请查阅相应文档。
博主在 Github 上有一个基于 SSM(Spring、Spring MVC、MyBatis)的小项目,部分学习内容会在该项目中使用。
该项目 Spring 相关配置,完全使用基于注解的方式。博主在刚接触各种配置的时候,绕了一些弯路。
对于刚接触这些框架的朋友,该项目或许会有些许帮助。如果在理解该项目时或参考时遇到任何问题,欢迎通过你能找到的任何方式联系博主,非常乐意共同学习。
项目地址为:spittr 。
如果你喜欢 xml 配置的方式,可参考另外一个项目 seckill 。该项目是博主在慕课网上学习该课程的源代码,项目中没有完全采用基于注解的方式,相比而言,该项目在配置方面更加老道
参考资料:
Spring 对缓存的抽象的更多相关文章
- 注释驱动的 Spring cache 缓存介绍
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- [转]注释驱动的 Spring cache 缓存介绍
原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...
- 注释驱动的 Spring cache 缓存介绍--转载
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- Spring实战——缓存
缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis-- 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓存技术的优势就是快,无需反复查询等. 当然 ...
- Spring cache 缓存
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- Spring Boot 缓存的基本用法
目录 一.目的 二.JSR-107 缓存规范 三.Spring 缓存抽象 四.Demo 1.使用 IDEA 创建 Spring Boot 项目 2.创建相应的数据表 3.创建 Java Bean 封装 ...
- Spring Cache缓存注解
目录 Spring Cache缓存注解 @Cacheable 键生成器 @CachePut @CacheEvict @Caching @CacheConfig Spring Cache缓存注解 本篇文 ...
- 谈谈spring的缓存
缓存到底扮演了什么角色 请移步: http://hacpai.com/article/1376986299174 在对项目进行优化的时候,我们可以主要从以下三个方面入手: 1 缓存 2 集群 3 异 ...
- Spring之缓存注解@Cacheable
https://www.cnblogs.com/fashflying/p/6908028.html https://blog.csdn.net/syani/article/details/522399 ...
随机推荐
- python 密码学编程
最近在看一本书.名字是 python密码学编程.在此做一些笔记,同时也为有需要的人提供一些参考. *************************************************** ...
- Java中synchronized和Lock的区别
synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和 ...
- ABP+AdminLTE+Bootstrap Table权限管理系统第四节--仓储,服务,服务接口及依赖注入
在ABP框架中,仓储,服务,这块算是最为重要一块之一了.ABP框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块,一个程序集可看成一个模块, 一个模块可以通过一个类来定义这个模块,而给定义这 ...
- HighGUI 图形用户界面
参考 自opencv3编程入门一书 图形用户界面主要内容包括图像载入,显示和输出到文件 在opencv1.0时代 图像的存储结构体都是IplImage × 在2.0以后,采用Mat 类作为图像存储的数 ...
- 社群系统ThinkSNS V4.5.29 APP更新发布,新增用户认证及系统消息
社群系统ThinkSNS V4版本移动端APP将于7月29日更新发布,本次更新修复部分bug,最主要是增加了移动端APP的用户认证功能,以及添加了系统消息,为V4.5.29版本发布.这两个功能PC端的 ...
- 深入理解Java 虚拟机阅读笔记(一)
1.程序计数器- 占用空间:较小 作用:字节码行号指示器 作用详情:指示指令执行,如(字节码的执行,分支,循环,跳转,异常处理,线程恢复) 特点:线程私有(每个计数器独立计算,上下文相互独立). 2. ...
- Java开发工具(Eclipse中内容辅助键的使用)
* A:Alt+/ 起提示作用* B:main+alt+/,syso+alt+/,给出其他提示* C:补充输出语句,选中需要输出的部分,alt+/选择最后一项即可* C:定义自己的alt + / * ...
- JQuery 思维导图
JQuery 就这么点内容,但是他能做出很多很多的效果.
- Go语言数组的使用
Go 语言数组 Go 语言提供了数组类型的数据结构. 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形.字符串或者自定义类型. 相对于去声明number0 ...
- easyui+ztree 后台管理系统模板
用easyui+ztree做了个后台管理系统模板,效果图: 下载地址: csdn:http://download.csdn.net/detail/jackpay/6744505 github:http ...