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.Cacheorg.springframework.cache.CacheManager 这两个接口体现。

虽然 Spring 的缓存抽象本身不提供具体的存储实现,但是 Spring 之外有很多实现,如:EhcacheCaffeineguaua等。

注意,Spring 的 cache 抽象并没有针对多线程、多进行环境做特殊处理,这样的特性由具体的 cache 实现来处理。

如果处于一个多进程的环境,比如你的应用部署在多个节点上,你就必须处理同步问题,以免造成节点间数据不一致。

要使用 cache 抽象,开发者需要关注两个方面:

  1. 缓存声明:表明某个方法需要缓存,及缓存策略;
  2. 缓存配置:缓存数据保存在哪里,从哪里读取。

二、以基于注解的方式使用缓存

@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) {...}
}

那么我们来总结一下,在三个级别上为每个缓存操作进行自定义的途径:

  • 全局配置:CacheManagerKeyGenerator
  • 类级别:@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 对缓存的抽象的更多相关文章

  1. 注释驱动的 Spring cache 缓存介绍

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  2. [转]注释驱动的 Spring cache 缓存介绍

    原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...

  3. 注释驱动的 Spring cache 缓存介绍--转载

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  4. Spring实战——缓存

    缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis-- 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓存技术的优势就是快,无需反复查询等. 当然 ...

  5. Spring cache 缓存

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  6. Spring Boot 缓存的基本用法

    目录 一.目的 二.JSR-107 缓存规范 三.Spring 缓存抽象 四.Demo 1.使用 IDEA 创建 Spring Boot 项目 2.创建相应的数据表 3.创建 Java Bean 封装 ...

  7. Spring Cache缓存注解

    目录 Spring Cache缓存注解 @Cacheable 键生成器 @CachePut @CacheEvict @Caching @CacheConfig Spring Cache缓存注解 本篇文 ...

  8. 谈谈spring的缓存

    缓存到底扮演了什么角色 请移步:  http://hacpai.com/article/1376986299174 在对项目进行优化的时候,我们可以主要从以下三个方面入手: 1 缓存 2 集群 3 异 ...

  9. Spring之缓存注解@Cacheable

    https://www.cnblogs.com/fashflying/p/6908028.html https://blog.csdn.net/syani/article/details/522399 ...

随机推荐

  1. salesforce零基础学习(七十七)队列的实现以及应用

    队列和栈简单的区别为栈是后进先出,队列是先进先出.队列也是特殊的线性表,所以队列也分为顺序存储结构和链式存储结构.本篇主要描述顺序存储结构. 我们先假定一个队列里有5个元素,当我们添加新元素时,添加到 ...

  2. D重叠面积

    Description zjahstu是个很厚道的ACMer,O(∩_∩)O~..特为大家准备水题一道.. 题目很简单,两个矩形,告诉你矩形1,矩形2的面积和他们的总面积,请你求两矩形重叠部分的面积. ...

  3. IE浏览器-在Win7系统的安装和卸载

    关于安装 在虚拟机(VMware Workstation)的Win7操作系统里,安装从官网下载的IE9/10/11 For Win7,始终失败.于是通过其它途径搜索到IE9/10/11 For Win ...

  4. 设计模式(3)--FactoryMethod( [2] 工厂方法模式)--创建型

    1.模式定义: 工厂方法是针对每一种产品提供一个工厂类.通过不同的工厂实例来创建不同的产品实例. 2.模式特点: (1)工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承. (2 ...

  5. C++跨平台使用(安卓,iso等)

    1 C#调用C++接口总结 http://www.cnblogs.com/xtblog/p/5729541.html 2 java调用C++接口 http://www.cnblogs.com/liul ...

  6. C#多线程的用法4-线程间的协作lock快捷方式

    线程间协作还可通过lock(加锁)方式进行,lock属于C#的Monitor语法糖(Monitor后续讲解).使用简单快捷,如下: /// <summary> /// 多线程协作-lock ...

  7. EXT.NET高效开发(二)——封装函数

    在上一篇<EXT.NET高效开发(一)--概述>中,大致的介绍了一下EXT.NET.那么本篇就要继续完成未完成的事业了.说到高效开发,那就是八仙过海各显神通.比如使用代码生成器,这点大家可 ...

  8. 简单倒计时js代码

    //倒计时 var timer=null; var interval = 1000; function ShowCountDown(year,month,day,hour,minute,second, ...

  9. mysql 存储过程中使用游标中使用临时表可以替代数组效果

    mysql不支持数组.但有时候需要组合几张表的数据,在存储过程中,经过比较复杂的运算获取结果直接输出给调用方,比如符合条件的几张表的某些字段的组合计算,mysql临时表可以解决这个问题.临时表:只有在 ...

  10. ORACLE概要文件

    oracle系统为了合理分配和使用系统的资源提出了概要文件的概念.所谓概要文件,就是一份描述如何使用系统的资源(主要是CPU资源)的配置文件.将概要文件赋予某个数据库用户,在用户连接并访问数据库服务器 ...