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. 奇怪的道路[JXOI2012]

    题目描述 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期有n座城市,编号为1..n.m条道路连接在这些城市之间,每条道路将两个城 ...

  2. 模板 mú bǎn

    链式前向星 #include<string.h> #define MAX 10000 struct node { int to,nex,wei; }edge[MAX*+]; ],cnt; ...

  3. React Native 系列(四) -- 布局

    前言 本系列是基于React Native版本号0.44.3写的.RN支持CSS中的布局属性,因此可以使用CSS布局属性,这里就不详细地讲解了,这篇文章的重点主要是讲述一下RN中的Flex布局. CS ...

  4. NYOJ--27--dfs--水池数目

    /* Name: NYOJ--27--水池数目 Author: shen_渊 Date: 17/04/17 15:42 Description: 经典dfs水题,,, */ #include<i ...

  5. 日本語の文法⇒ day1 限る型…意向形の紹介

    1)限る型 ① 不仅仅 名+に限らず この漫画は子供に限らず.大人にも人気が高い. 最近は女性に限らず.男もダイエットに励む人が多い. ②仅仅....名+限って 当店は.本日に限って半額をセールを実施 ...

  6. pwnable.kr memcpy之write up

    // compiled with : gcc -o memcpy memcpy.c -m32 -lm #include <stdio.h> #include <string.h> ...

  7. QT 延时函数设置

    QT 的延时函数分为非阻塞延时 和 阻塞型延时 非阻塞延时: void GreenPass3::delaymsec(int msec){    QTime dieTime = QTime::curre ...

  8. Fiddler过滤会话

    Fiddler每次打开的时候都会打开十多个会话,期望只想抓取自己想要的请求.   1)User Filters:启用过滤器 2)在Filers面板中勾选"Use Filters", ...

  9. java基础(数据类型,运算符)

    java基础之数据类型(变量) java中存在的数据有常量和变量 常量:在程序执行的过程中其值不可以发生改变 常量的分类 字面值常量 字面值常量的分类 常量 描述 字符串常量 用双引号括起来的内容 整 ...

  10. .net到Java那些事儿--structs做了那些事(二)

    一.跟着项目先来看下structs怎么执行的      首先看下web.xml配置文件,下面有如下代码 <filter> <filter-name>struts2</fi ...