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 ...
随机推荐
- K个最近的点
前段时间在网上看到一些准备找工作的人会在LintCode上刷题,然后我今天上去看了一下,也打算开始做题,然后把每天做的题目和以后的优化记录下来. 2017年8月6日 21:17:27 第一题: 描述: ...
- ABP+AdminLTE+Bootstrap Table权限管理系统第二节--数据库脚本
第一点,上一篇文章中我们讲到codefirst中一些问题包括如图 1,codefirst在执行的数据库迁移过程中产生了很多文件,对于强迫症的我而言特别不爽,这些是可以不用生成的啊 2,在codefir ...
- Vue事件处理
前面的话 Vue事件监听的方式貌似违背了关注点分离(separation of concern)的传统理念.实际上,所有的Vue.js事件处理方法和表达式都严格绑定在当前视图的ViewModel上,它 ...
- python基础===Python性能优化的20条建议
优化算法时间复杂度 算法的时间复杂度对程序的执行效率影响最大,在Python中可以通过选择合适的数据结构来优化时间复杂度,如list和set查找某一个元素的时间复杂度分别是O(n)和O(1).不同的场 ...
- 关于 Overtrue 的拼音库 overtrue/pinyin 为何 travis 为 error【社交系统研发日记十】
什么是ThinkSNS ? ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+(简称TS+).社交系统 ...
- KMS注册
--KMS注册 -------------2014/03/25 --Read this First http://social.technet.microsoft.com/wiki/contents/ ...
- NoSql数据库简介及Redis学习
NO-Sql数据库:Not Only不仅仅是SQL 定义:非关系型数据库:NoSQL用于超大规模数据的存储.(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据).这些类型的数据存储不需要固 ...
- leetcode range sum query
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- 一些常用的集合工具的代码块(缓慢更新XD)
鱼的记忆 我发现在项目中常常要用到一些集合的处理,不同的项目我经常会编写自己的集合工具代码块,后来我发现我总是在写一样的代码块(可能是我记性不好吧:),毕竟鱼的记忆只有7秒),所以我意识到了是时候 ...
- MFC加载PNG图片并实现双缓冲
因为PNG包含Alpha通道,所以不同于BITMAP,在MFC中使用CImage类对其进行处理,通常使用load和draw成员函数. 所以标题的论述可以进一步解释为,使用CImage实现双缓冲. 通常 ...