|
| 1 | +# JSR107中文版(非官方) |
| 2 | + |
| 3 | +最近在学缓存这块,没找到现成的中文文档,翻译一份给大家。最终版有多个,这里翻译的是[JSR107 Specification 1.1.1 Maintenance Release - Google Doc](https://docs.google.com/document/d/1ijduF_tmHvBaUS7VBBU2ZN8_eEBiFaXXg9OI0_ZxCrA/edit?usp=sharing)。 |
| 4 | + |
| 5 | +本文内容仅限于学习使用,为了便于理解,翻译并不是单词对汉语的直译,建议阅读时和英文原文进行对照。 |
| 6 | + |
| 7 | +## 引言 |
| 8 | + |
| 9 | +本规范描述了 Java缓存API 的目标和功能。 |
| 10 | + |
| 11 | +Java缓存API 为 Java 程序提供了可用于从缓存中创建,访问,更新和删除条目的统一抽象。 |
| 12 | + |
| 13 | +### 概览 |
| 14 | + |
| 15 | +缓存在提高应用程序性能和可伸缩性上非常有效。 |
| 16 | + |
| 17 | +缓存在是将数据副本以低延迟能够获取的结构保存一段时间,以便于请求相同信息时能够更快响应。 |
| 18 | + |
| 19 | +缓存适用于那些创建或访问代价比较高的应用。比如一个频繁访问的 JavaWeb Servlet 网页,里面包括多次数据库访问,网络请求,和比较耗时的计算任务;中间用到的数据可能是可以直接重用的,把这些信息缓存起来,下次访问时直接读取可以减少页面构建的时间。 |
| 20 | + |
| 21 | +Java缓存API提供了一种使用缓存的通用方式,从而使开发者专注于应用本身的开发。本规范定义了缓存的术语、语义和响应的Java接口 |
| 22 | + |
| 23 | +### 什么是缓存 |
| 24 | + |
| 25 | +缓存在计算中无处不在。在应用程序设计领域中,它通常代表开发者使用内存或低延迟的数据结构来临时存储缓存数据的副本或引用,以便以后进行重用来减少重新访问或重新创建的成本。 |
| 26 | + |
| 27 | +在 Java缓存API领域中,术语“缓存”代表的是Java开发者使用缓存组件临时缓存Java对象的技术。 |
| 28 | + |
| 29 | +通常缓存的是数据库的数据,但这并不是必须的,任何创建或访问比较昂贵或耗时的数据都可以进行缓存,比如: |
| 30 | + |
| 31 | +1. Web服务调用的客户端缓存 |
| 32 | +2. 昂贵的计算,例如渲染的图像 |
| 33 | +3. 数据缓存 |
| 34 | +4. servlet响应缓存 |
| 35 | +5. 领域对象图(caching of domain object graphs) |
| 36 | + |
| 37 | +### 目标 |
| 38 | + |
| 39 | +Java缓存AIP 的目标是: |
| 40 | + |
| 41 | +1. 为应用程序提供缓存功能,尤其是缓存Java对象的能力。 |
| 42 | +2. 定义一套通用的缓存概念和设施。 |
| 43 | +3. 减少Java开发人员采用缓存的学习成本。 |
| 44 | +4. 最大化应用程序切换缓存实现的能力。 |
| 45 | +5. 支持进程内和分布式缓存实现。 |
| 46 | +6. 支持按值和(可选)按引用缓存Java对象。 |
| 47 | +7. 参照 JSR-175 定义运行时缓存注解,以便开发者可以使用注解处理器来支持声明式缓存。 |
| 48 | + |
| 49 | +### 非目标 |
| 50 | + |
| 51 | +Java缓存API 无法解决的问题: |
| 52 | + |
| 53 | +1. 资源和内存限制配置。很多缓存实现都提供了约束运行时可使用资源的配置,但是这并不是本规范的内容。本规范只提供了一种标准机制使开发者去指定数据缓存的时长。 |
| 54 | +2. 缓存数据的存储形式。本规范未指定缓存实现如何存储或表示缓存的数据。 |
| 55 | +3. 管理。本规范并未规定如何管理缓存。仅定义了编程式配置缓存的机制和通过 JMX 来操作缓存统计信息的机制。 |
| 56 | +4. 安全性。本规范未指定如何保护缓存内容或如何控制对缓存的访问和操作。 |
| 57 | +5. 缓存和数据源数据同步。本规范未指定应用程序或缓存实现应如何使缓存和数据源数据保持一致。尽管开发者可以使用 `read-through` 和 `write-through` 技术,但是这些技术只能保证数据通过缓存更新数据源时的一致性,如果应用程序不经过缓存直接写数据源,此时缓存和数据源的数据一致性就无法保证。 |
| 58 | + |
| 59 | +### Java SE 和 Java EE 支持 |
| 60 | + |
| 61 | +Java缓存API兼容标准版和企业版(版本6或更高版本)的应用程序。缓存实现可以选择只在更高版本的Java上运行,可以支持使用 JavaEE 的应用,但是本规范并未定义如何实现。 |
| 62 | + |
| 63 | +### 包名 |
| 64 | + |
| 65 | +顶级包名是 `javax.cache`。 |
| 66 | + |
| 67 | +### 可选特性 |
| 68 | + |
| 69 | +本规范中的所有特性都是强制性必须实现的,但 `OptionalFeature` 枚举中列出的功能除外: |
| 70 | + |
| 71 | +`storeByReference` |
| 72 | + |
| 73 | +如果实现,则必须完全按照本规范中的描述来实现。 |
| 74 | + |
| 75 | +开发者可以使用 `cachingProvider.isSupported(OptionalFeature feature)` 确定缓存提供程序已实现了哪些可选功能。 |
| 76 | + |
| 77 | +一些可选功能仅在特定情况下才有意义。 例如,分布式缓存通常不支持 `storeByReference`。 |
| 78 | + |
| 79 | +可选功能允许缓存实现不必支持所有功能,并且允许最终用户和框架发现支持的特性是什么,以便他们可以动态配置适当的用法。 |
| 80 | + |
| 81 | +### 文档规范 |
| 82 | + |
| 83 | +`Arial (11磅)` 字体用于该规范的正文。 |
| 84 | + |
| 85 | +斜体 `Arial (11磅)` 字体用于包含非正文性信息的段落,例如描述典型用法的注释或使用说明性规范澄清文本的注释。 |
| 86 | + |
| 87 | +`Courier New(11磅)` 字体用于代码。 Java代码,示例和示例数据片段也使用Courier New字体。 格式如下(10点字体): |
| 88 | + |
| 89 | +```java |
| 90 | +package com.example.hello; |
| 91 | +public class Hello { |
| 92 | + public static void main(String args[] { |
| 93 | + System.out.println(“Hello Worlds”); |
| 94 | + } |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +另外,这些关键字‘MUST’, ‘MUST NOT’, ‘REQUIRED’, ‘SHALL’, ‘SHALL NOT’, ‘SHOULD’, ‘SHOULD NOT’, ‘RECOMMENDED’, ‘MAY’, and ‘OPTIONAL’ 应按照[RFC 2119](https://tools.ietf.org/html/rfc2119) 中的说明进行解释。 |
| 99 | + |
| 100 | +### 专家组成员 |
| 101 | + |
| 102 | +This specification is being developed under the Java Community Process v2.9. |
| 103 | + |
| 104 | +Leading experts throughout the entire Java community have come together to build this Java caching standard. |
| 105 | + |
| 106 | +The following are expert group members: |
| 107 | + |
| 108 | +- Greg Luck |
| 109 | +- Brian Oliver, Oracle |
| 110 | +- Cameron Purdy, Oracle |
| 111 | +- Galder Zamarreño, Red Hat |
| 112 | +- Nikita Ivanov, Grid Gain |
| 113 | +- Chris Berry |
| 114 | +- Jon Stevens |
| 115 | +- Rick Hightower |
| 116 | +- Ben Cotton, Credit Suisse |
| 117 | +- David Mossakowski, Citigroup |
| 118 | +- Bongjae Chang |
| 119 | +- Steve Millidge |
| 120 | +- Gabe Montero, IBM |
| 121 | +- Brian Martin, IBM |
| 122 | +- Eric Dalquist |
| 123 | +- Pete Muir, Red Hat, Inc. |
| 124 | +- William Newport, Goldman Sachs |
| 125 | +- Ryan Gardner, Dealer.com |
| 126 | +- Chris Dennis, Terracotta, Inc. |
| 127 | +- Toshio Takeda, Fujitsu |
| 128 | +- Chang Paek, TmaxSoft, Inc. |
| 129 | + |
| 130 | +The following are official observers: |
| 131 | + |
| 132 | +- Linda DeMichiel, Oracle |
| 133 | +- Bill Shannon, Oracle |
| 134 | +- Jens Wilke (Jens contributed very heavily to the 1.1 MR) |
| 135 | + |
| 136 | +### 致谢 |
| 137 | + |
| 138 | +During the course of the JSR we have received many excellent suggestions on the JSR mailing lists. Thanks to those people. |
| 139 | + |
| 140 | +## 基本原理 |
| 141 | + |
| 142 | +### 核心概念 |
| 143 | + |
| 144 | +Java Caching API定义了五个核心接口:`CachingProvider`,`CacheManager`,`Cache`,`Entry`和`ExpiryPolicy`。 |
| 145 | + |
| 146 | +`CachingProvider` 用于创建,配置,获取,管理和控制零个或多个`CacheManager` 。应用程序可以在运行时访问和使用零个或多个`CachingProvider`。 |
| 147 | + |
| 148 | +`CacheManager` 用于创建,配置,获取,管理和控制零个或更多个唯一命名的 `Cache`。 `CacheManager` 由单个 `CachingProvider` 拥有。 |
| 149 | + |
| 150 | +`Cache`是一种类似于 `Map` 的数据结构,它允许临时存储基于键的值。`Cache` 由单个`CacheManager` 拥有。 |
| 151 | + |
| 152 | +`Entry` 是由 `Cache` 存储的单个键值对。 |
| 153 | + |
| 154 | +`Cache` 中的每个 `Entry` 都有一个有效时间,在此期间可以进行访问,更新和删除操作。一旦超过此持续时间,就称该条目已过期。一旦过期,条目将不再可用于访问,更新或删除,就好像它们从未存在于缓存中一样。使用 `ExpiryPolicy` 设置到期时间。 |
| 155 | + |
| 156 | + |
| 157 | + |
| 158 | +[图片源文件](../../res/jsr107-core-interface-class-diagram.pu) |
| 159 | + |
| 160 | +### 按值存储和按引用存储 |
| 161 | + |
| 162 | +`Cache` 存储 `Entry` 的方式有两种,一个是按值存储,一个是按引用存储 |
| 163 | + |
| 164 | +`javax.cache.configuration.MutableConfiguration#isStoreByValue` 字段定义了是否按值存储,可以在创建 `Cache` 时进行配置。默认是 `true` 代表安值存储 |
| 165 | + |
| 166 | +按值存储:将应用程序提供的键和值存储缓存之前先对其进行复制,访问时从缓存中返回条目的新副本。复制条目(存储在缓存中)以及从缓存返回时再次复制条目的目的是允许应用程序继续更改键和值的状态,而不会对缓存所保存的条目产生副作用。 |
| 167 | + |
| 168 | +Java序列化是实现键和值副本的一种简单方法。 |
| 169 | + |
| 170 | +为了确保实现之间的可移植性,建议在使用按值存储时,自定义键和值类实现并采用标准Java序列化。 |
| 171 | + |
| 172 | +实现用来复制键和条目值的机制可以是可自定义的。但是,为了确保应用程序的可移植性,实现必须允许应用程序仅使用标准Java序列化。实现不得强制应用程序采用非标准Java序列化。 |
| 173 | + |
| 174 | +按引用存储是一种可选方案,它代表 `Cache` 实现仅存储和返回对应用程序提供的键和值的引用,而不是按按值存储方法进行复制。如果应用程序稍后使用按引用存储的语义来更改提供给缓存的键或值,则那些从缓存访问条目的人将可以看到突变的副作用,而无需应用程序更新缓存。 |
| 175 | + |
| 176 | +对于在Java堆上实现的缓存,按引用存储相对更快。 |
| 177 | + |
| 178 | +Tips: |
| 179 | + |
| 180 | +> Heap only: When using heap only caches, the default is by-reference unless you configure a Copier. |
| 181 | +> |
| 182 | +> Ehcache 3.8 仅使用堆存储层时,默认使用按引用存储,除非显式配置了一个 Copier |
| 183 | +> |
| 184 | +> https://www.ehcache.org/documentation/3.8/107.html |
| 185 | +
|
| 186 | +```xml |
| 187 | +<default-copiers> |
| 188 | + <copier type="me.rainstorm.demo.cache.domain.Person">org.ehcache.impl.copy.SerializingCopier</copier> |
| 189 | +</default-copiers> |
| 190 | +``` |
| 191 | + |
| 192 | +### 缓存与 `Map` |
| 193 | + |
| 194 | +缓存和 `Map` API 相似,下面简单描述主要的异同点 |
| 195 | + |
| 196 | +相同点: |
| 197 | + |
| 198 | +1. 通过 key 进行存储和访问 |
| 199 | +2. 每个 key 仅与一个值进行关联 |
| 200 | +3. 可变对象作为 key 时需要特别注意,如果 key 发生了变化,且变化对 `equals` 方法有影响,此时缓存的行为是未定义的。 |
| 201 | +4. 缓存使用相等性来判断给定的 key 或 value 是否已存在,自定义的类做为 key 或 value 时 推荐实现合适的 `Object.equals` 和 `Object.hashCode` 方法。 |
| 202 | + |
| 203 | +不同点: |
| 204 | + |
| 205 | +1. 缓存键和值不能为null。 |
| 206 | + - 任何尝试为键或值使用null都会导致抛出NullPointerException |
| 207 | +2. 条目可能会过期。 |
| 208 | + - 确保条目不再对应用程序有效(因为它们不再被视为有效)的过程称为“到期” |
| 209 | +3. 条目可能会被驱逐。 |
| 210 | + - 缓存通常用于存储整个数据集经常使用的一小部分子集,当空间不够时就会按照一定策略进行驱逐条目。 |
| 211 | + - 当缓存超过资源限制时从缓存中删除条目的过程称为“逐出(eviction)”。当由于资源限制而从缓存中删除条目时,该条目被称为“被驱逐(evicted)”。 |
| 212 | + - 虽然规范没有定义缓存的容量,但是推荐缓存实现提供一种当达到容量限制时,结合适当的驱逐策略,来选择和驱逐条目的机制。例如:LRU逐出策略试图逐出最近最少使用的条目。 |
| 213 | + - 规格中未定义容量的一些原因是: |
| 214 | + - 实现可以利用多层分层存储结构,从而定义每层的容量。在这种情况下,无法定义缓存的整体容量。 |
| 215 | + - 实现可以按字节而不是每个层上的条目数定义容量。 |
| 216 | + - 就内存而言,条目的相对成本与运行时条目实现的内部表示直接相关。 |
| 217 | +4. 为了支持原子比较并交换(CAS)操作,自定义值类应提供 `Object.equals` 的适当实现。 |
| 218 | +5. 实现可能要求键和值以某种方式可序列化。 |
| 219 | +6. 缓存可以配置为使用按值存储或可选地使用按引用存储来控制条目的存储方式。 |
| 220 | +7. 实现可以选择强制实施安全性限制。 如果发生违规,则必须抛出`SecurityException`。 |
| 221 | + |
| 222 | +尽管建议这样做,但缓存实现调用由自定义键类定义的 `Object.hashCode` 或 `Object.equals` 方法并不是必须的。缓存实现可以自由优化,从而避免调用这些方法。 |
| 223 | + |
| 224 | +由于本规范未定义对象等效性的概念,因此,依赖于缓存实现的等效性优化来确定自定义键类等效性的应用程序可能无法移植。 |
| 225 | + |
| 226 | +### 一致性 |
| 227 | + |
| 228 | + |
| 229 | + |
| 230 | +## 参考 |
| 231 | + |
| 232 | +1. [JSR107 Specification 1.1.1 Maintenance Release - Google Doc](https://docs.google.com/document/d/1ijduF_tmHvBaUS7VBBU2ZN8_eEBiFaXXg9OI0_ZxCrA/edit?usp=sharing) |
| 233 | +1. [jsr107/jsr107spec - GitHub](https://github.com/jsr107/jsr107spec) |
| 234 | +1. [jsr107 - jcp.org](https://jcp.org/aboutJava/communityprocess/final/jsr107/index.html) |
0 commit comments