Java 缓存规范中文版 (JSR-107)

栏目: IT技术 · 发布时间: 4年前

内容简介:最近在学缓存这块,没找到现成的中文文档,翻译一份给大家。最终版有多个,这里翻译的是本文内容仅限于学习使用,为了便于理解,翻译并不是单词对汉语的直译,建议阅读时和英文原文进行对照。完结总评,有两点感觉:

JSR107中文版(非官方)

最近在学缓存这块,没找到现成的中文文档,翻译一份给大家。最终版有多个,这里翻译的是 JSR107 Specification 1.1.1 Maintenance Release - Google Doc

本文内容仅限于学习使用,为了便于理解,翻译并不是单词对汉语的直译,建议阅读时和英文原文进行对照。

完结总评,有两点感觉:

  1. 感觉有点臃肿,对缓存的定义过于宽泛,从进程内缓存到跨进程缓存到分布式缓存,但截止目前,工作中真正用到的部分只有进程内缓存
  2. 管理部分基于 JMX,有点老旧

我还是看看 Caffeine,感觉可能这个更好一点。

引言

本规范描述了 Java 缓存API 的目标和功能。

Java缓存API 为 Java 程序提供了可用于从缓存中创建,访问,更新和删除条目的统一抽象。

概览

缓存在提高应用程序性能和可伸缩性上非常有效。

缓存在是将数据副本以低延迟能够获取的结构保存一段时间,以便于请求相同信息时能够更快响应。

缓存适用于那些创建或访问代价比较高的应用。比如一个频繁访问的 JavaWeb Servlet 网页,里面包括多次数据库访问,网络请求,和比较耗时的计算任务;中间用到的数据可能是可以直接重用的,把这些信息缓存起来,下次访问时直接读取可以减少页面构建的时间。

Java缓存API提供了一种使用缓存的通用方式,从而使开发者专注于应用本身的开发。本规范定义了缓存的术语、语义和响应的Java接口

什么是缓存

缓存在计算中无处不在。在应用程序设计领域中,它通常代表开发者使用内存或低延迟的数据结构来临时存储缓存数据的副本或引用,以便以后进行重用来减少重新访问或重新创建的成本。

在 Java缓存API领域中,术语“缓存”代表的是Java开发者使用缓存组件临时缓存Java对象的技术。

通常缓存的是数据库的数据,但这并不是必须的,任何创建或访问比较昂贵或耗时的数据都可以进行缓存,比如:

  1. Web服务调用的客户端缓存
  2. 昂贵的计算,例如渲染的图像
  3. 数据缓存
  4. servlet响应缓存
  5. 领域对象图(caching of domain object graphs)

目标

Java缓存AIP 的目标是:

  1. 为应用程序提供缓存功能,尤其是缓存Java对象的能力。
  2. 定义一套通用的缓存概念和设施。
  3. 减少Java开发人员采用缓存的学习成本。
  4. 最大化应用程序切换缓存实现的能力。
  5. 支持进程内和分布式缓存实现。
  6. 支持按值和(可选)按引用缓存Java对象。
  7. 参照 JSR-175 定义运行时缓存注解,以便开发者可以使用注解处理器来支持声明式缓存。

非目标

Java缓存API 无法解决的问题:

  1. 资源和内存限制配置。很多缓存实现都提供了约束运行时可使用资源的配置,但是这并不是本规范的内容。本规范只提供了一种标准机制使开发者去指定数据缓存的时长。
  2. 缓存数据的存储形式。本规范未指定缓存实现如何存储或表示缓存的数据。
  3. 管理。本规范并未规定如何管理缓存。仅定义了编程式配置缓存的机制和通过 JMX 来操作缓存统计信息的机制。
  4. 安全性。本规范未指定如何保​​护缓存内容或如何控制对缓存的访问和操作。
  5. 缓存和数据源数据同步。本规范未指定应用程序或缓存实现应如何使缓存和数据源数据保持一致。尽管开发者可以使用 read-throughwrite-through 技术,但是这些技术只能保证数据通过缓存更新数据源时的一致性,如果应用程序不经过缓存直接写数据源,此时缓存和数据源的数据一致性就无法保证。

Java SE 和 Java EE 支持

Java缓存API兼容标准版和企业版(版本6或更高版本)的应用程序。缓存实现可以选择只在更高版本的Java上运行,可以支持使用 JavaEE 的应用,但是本规范并未定义如何实现。

包名

顶级包名是 javax.cache

可选特性

本规范中的所有特性都是强制性必须实现的,但 OptionalFeature 枚举中列出的功能除外:

storeByReference

如果实现,则必须完全按照本规范中的描述来实现。

开发者可以使用 cachingProvider.isSupported(OptionalFeature feature) 确定缓存提供程序已实现了哪些可选功能。

一些可选功能仅在特定情况下才有意义。 例如,分布式缓存通常不支持 storeByReference

可选功能允许缓存实现不必支持所有功能,并且允许最终用户和框架发现支持的特性是什么,以便他们可以动态配置适当的用法。

文档规范

Arial (11磅) 字体用于该规范的正文。

斜体 Arial (11磅) 字体用于包含非正文性信息的段落,例如描述典型用法的注释或使用说明性规范澄清文本的注释。

Courier New(11磅) 字体用于代码。 Java代码,示例和示例数据片段也使用Courier New字体。 格式如下(10点字体):

package com.example.hello;
public class Hello {
    public static void main(String args[] {
        System.out.println(“Hello Worlds”);
    }
}

另外,这些关键字‘MUST’, ‘MUST NOT’, ‘REQUIRED’, ‘SHALL’, ‘SHALL NOT’, ‘SHOULD’, ‘SHOULD NOT’, ‘RECOMMENDED’, ‘MAY’, and ‘OPTIONAL’ 应按照 RFC 2119 中的说明进行解释。

专家组成员

This specification is being developed under the Java Community Process v2.9.

Leading experts throughout the entire Java community have come together to build this Java caching standard.

The following are expert group members:

  • Greg Luck
  • Brian Oliver, Oracle
  • Cameron Purdy, Oracle
  • Galder Zamarreño, Red Hat
  • Nikita Ivanov, Grid Gain
  • Chris Berry
  • Jon Stevens
  • Rick Hightower
  • Ben Cotton, Credit Suisse
  • David Mossakowski, Citigroup
  • Bongjae Chang
  • Steve Millidge
  • Gabe Montero, IBM
  • Brian Martin, IBM
  • Eric Dalquist
  • Pete Muir, Red Hat, Inc.
  • William Newport, Goldman Sachs
  • Ryan Gardner, Dealer.com
  • Chris Dennis, Terracotta, Inc.
  • Toshio Takeda, Fujitsu
  • Chang Paek, TmaxSoft, Inc.

The following are official observers:

  • Linda DeMichiel, Oracle
  • Bill Shannon, Oracle
  • Jens Wilke (Jens contributed very heavily to the 1.1 MR)

致谢

During the course of the JSR we have received many excellent suggestions on the JSR mailing lists. Thanks to those people.

基本原理

核心概念

Java Caching API定义了五个核心接口: CachingProviderCacheManagerCacheEntryExpiryPolicy

CachingProvider 用于创建,配置,获取,管理和控制零个或多个 CacheManager 。应用程序可以在运行时访问和使用零个或多个 CachingProvider

CacheManager 用于创建,配置,获取,管理和控制零个或更多个唯一命名的 CacheCacheManager 由单个 CachingProvider 拥有。

Cache 是一种类似于 Map 的数据结构,它允许临时存储基于键的值。 Cache 由单个 CacheManager 拥有。

Entry 是由 Cache 存储的单个键值对。

Cache 中的每个 Entry 都有一个有效时间,在此期间可以进行访问,更新和删除操作。一旦超过此持续时间,就称该条目已过期。一旦过期,条目将不再可用于访问,更新或删除,就好像它们从未存在于缓存中一样。使用 ExpiryPolicy 设置到期时间。

Java 缓存规范中文版 (JSR-107)

图片源文件

按值存储和按引用存储

Cache 存储 Entry 的方式有两种,一个是按值存储,一个是按引用存储

javax.cache.configuration.MutableConfiguration#isStoreByValue 字段定义了是否按值存储,可以在创建 Cache 时进行配置。默认是 true 代表安值存储

按值存储:将应用程序提供的键和值存储缓存之前先对其进行复制,访问时从缓存中返回条目的新副本。复制条目(存储在缓存中)以及从缓存返回时再次复制条目的目的是允许应用程序继续更改键和值的状态,而不会对缓存所保存的条目产生副作用。

Java序列化是实现键和值副本的一种简单方法。

为了确保实现之间的可移植性,建议在使用按值存储时,自定义键和值类实现并采用标准Java序列化。

实现用来复制键和条目值的机制可以是可自定义的。但是,为了确保应用程序的可移植性,实现必须允许应用程序仅使用标准Java序列化。实现不得强制应用程序采用非标准Java序列化。

按引用存储是一种可选方案,它代表 Cache 实现仅存储和返回对应用程序提供的键和值的引用,而不是按按值存储方法进行复制。如果应用程序稍后使用按引用存储的语义来更改提供给缓存的键或值,则那些从缓存访问条目的人将可以看到突变的副作用,而无需应用程序更新缓存。

对于在Java堆上实现的缓存,按引用存储相对更快。

Tips:

Heap only: When using heap only caches, the default is by-reference unless you configure a Copier.

Ehcache 3.8 仅使用堆存储层时,默认使用按引用存储,除非显式配置了一个 Copier

https://www.ehcache.org/documentation/3.8/107.html

<default-copiers>
    <copier type="me.rainstorm.demo.cache.domain.Person">org.ehcache.impl.copy.SerializingCopier</copier>
</default-copiers>

缓存与 Map

缓存和 Map API 相似,下面简单描述主要的异同点

相同点:

  1. 通过 key 进行存储和访问
  2. 每个 key 仅与一个值进行关联
  3. 可变对象作为 key 时需要特别注意,如果 key 发生了变化,且变化对 equals 方法有影响,此时缓存的行为是未定义的。
  4. 缓存使用相等性来判断给定的 key 或 value 是否已存在,自定义的类做为 key 或 value 时 推荐实现合适的 Object.equalsObject.hashCode 方法。

不同点:

  1. 缓存键和值不能为null。
    • 任何尝试为键或值使用null都会导致抛出NullPointerException
  2. 条目可能会过期。
    • 确保条目不再对应用程序有效(因为它们不再被视为有效)的过程称为“到期”
  3. 条目可能会被驱逐。
    • 缓存通常用于存储整个数据集经常使用的一小部分子集,当空间不够时就会按照一定策略进行驱逐条目。
    • 当缓存超过资源限制时从缓存中删除条目的过程称为“逐出(eviction)”。当由于资源限制而从缓存中删除条目时,该条目被称为“被驱逐(evicted)”。
    • 虽然规范没有定义缓存的容量,但是推荐缓存实现提供一种当达到容量限制时,结合适当的驱逐策略,来选择和驱逐条目的机制。例如:LRU逐出策略试图逐出最近最少使用的条目。
    • 规格中未定义容量的一些原因是:
      • 实现可以利用多层分层存储结构,从而定义每层的容量。在这种情况下,无法定义缓存的整体容量。
      • 实现可以按字节而不是每个层上的条目数定义容量。
      • 就内存而言,条目的相对成本与运行时条目实现的内部表示直接相关。
  4. 为了支持原子比较并交换(CAS)操作,自定义值类应提供 Object.equals 的适当实现。
  5. 实现可能要求键和值以某种方式可序列化。
  6. 缓存可以配置为使用按值存储或可选地使用按引用存储来控制条目的存储方式。
  7. 实现可以选择强制实施安全性限制。 如果发生违规,则必须抛出 SecurityException

尽管建议这样做,但缓存实现调用由自定义键类定义的 Object.hashCodeObject.equals 方法并不是必须的。缓存实现可以自由优化,从而避免调用这些方法。

由于本规范未定义对象等效性的概念,因此,依赖于缓存实现的等效性优化来确定自定义键类等效性的应用程序可能无法移植。

一致性

一致性指的是,当多个线程同时访问一个缓存时,保证并发修改的可见性是一致的。

PS,这句话翻译的感觉不是特别好,放出原文。

Consistency refers to the behavior of caches and the guarantees that exist when concurrent cache mutation occur together with the visibility of the mutations when multiple threads are accessing a cache.

所有缓存实现必须要支持默认一致性模型。

默认一致性

使用默认的一致性模式时,大多数高速缓存操作在执行时,就好像缓存中的每个键都存在加锁机制一样。 当缓存操作获得对某个键的排他性读写锁时,对该键的所有后续操作都将阻塞,直至锁释放。 两个线程执行操作时,遵循 happen-before 原则(包括不同Java虚拟机中的线程)。

可以理解为一个悲观锁,以加锁,修改,解锁来保证一致性。

对于那些有返回值的缓存操作,返回的缓存值是最新的值,但是这个最新的值的定义,根据缓存实现的不同,可以选择操作前的值,也可以选择是操作后的值。

这可以理解为没有保证一致性的无锁方法。ps:不理解这句话

部分操作遵循不同的惯例,这些操作在条目的缓存值满足某种预期的状态时才进行修改。这些操作在多线程环境下好像共享一个锁,这个可以理解为是一种优化的加锁方式,这类加锁方式叫 CAS

boolean putIfAbsent(K key, V value);
boolean remove(K key, V oldValue);
boolean replace(K key, V oldValue, V newValue);
boolean replace(K key, V value);
V getAndReplace(K key, V value);

想其他缓存操作一样,CAS 操作在写新值时也需要表现的像是获得了一个排它锁。

在默认一致性下,尽管CAS方法可以允许更高级别的并发性,但它们将被非CAS方法所阻止。

As a result, in default consistency, while the CAS methods can allow a higher level of concurrency they will be held up by the non-CAS methods.

下表显示了适用于每个Cache方法的默认一致性。

方法 默认一致性
boolean containsKey(K key) 最新值
V get(K key) happen-before
Map<K,V> getAll(Collection<? extends K> keys) 对里面的每个key,而不是整个集合
V getAndPut(K key, V value) happen-before
V getAndRemove(K key) happen-before
V getAndReplace(K key, V value) happen-before + CAS
CacheManager getCacheManager() N/A
CacheConfiguration getConfiguration() N/A
String getName() N/A
Iterator<Cache.Entry<K, V>> iterator() 最新值
void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) N/A
void put(K key, V value) happen-before
void putAll(Map<? extends K,? extends V> map) 对里面的每个key,而不是整个集合
boolean putIfAbsent(K key, V value) happen-before + CAS
boolean remove(K key) happen-before
boolean remove(K key, V oldValue) happen-before + CAS
void removeAll() 最新值
void removeAll(Set<? extends K> keys) happen-before + CAS
<T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)entryProcessor); happen-before
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,EntryProcessor<K, V, T> entryProcessor, Object... arguments); 对里面的每个key,而不是整个集合
boolean replace(K key, V value) happen-before + CAS
boolean replace(K key, V oldValue, V newValue) happen-before + CAS
<T> T unwrap(Class<T> cls) N/A

高级一致性模型

具体实现可以自行拓展一致性模型。

缓存拓扑

尽管本规范并没有规定特定的缓存实现结构,但是显而易见的,缓存实体可以存储到当前进程本地和/或分布到多个进程。具体实现可以任意选择合适的结构。

这个概念在规范有多中表现形式:

  • 大多数修改方法要么返回 void 要么返回一个低成本的值。比如 java.util.Map 提供了 V put(K key, V value) ,但是 javax.cache.Cache 提供的是 void put(K key, V value) 。返回类型成本更高的版本也有提供,比如 V getAndPut(K key, V value) ,其中的返回值是旧值和 Map 相似。
  • 通过创建不假设实现将缓存数据放在进程内部的语义, Configuration 实现了 Serializable 接口已支持在网络中传输。开发者可以定义 CacheEntryListenerExpiryPolicyCacheEntryFilterCacheWriterCacheLoader 并将它们和 Cache 进行关联。为了支持分布式结构,开发者需要定义一个工场去创建它们,这个工厂需要实现 Serializable 接口。
  • 在整个规范中,对于那些可能很大的返回值和参数使用 Iterable 进行遍历。一个 Cache 可能很大以至于 key-set 不能完整的保存到可用内存中,并且网络也不好。 CacheCacheEntryListener 子接口上的监听器方法、 CacheLoader 的批处理方法都使用 Iterable 接口。
  • CacheEntryListener , ExpiryPolicy , CacheEntryFilter , CacheWriter , CacheLoader 这些类在哪里实现,什么时间实例化,和执行本规范不做限制。(在分布式实现中,这些可以全部与数据存放在一起,而不是随网络传输)
  • CachingProvider.getCacheManager(URI uri, ClassLoader classLoader) 对特定的 URIClassLoader 来说有唯一的 CacheManager ,这确保了实现能够实例化多个实例。

执行上下文

EntryProcessor , CacheEntryListener , CacheLoader , CacheWriter and ExpiryPolicy 的上下文是 CacheManager 这就意味着,在部署时,创建 CacheManagerClassLoader 需要能够访问应用程序类。

类的可用性这个目标如何实现可以由具体的实现自行定义。例如,在 Java EE 环境中,应用程序定义的上述类都包含在企业应用程序的 ear/war/jar 中。

尽管上述类和应用程序类使用同一个 ClassLoader ,从而获得直接访问所有资源的权限,但是,为了保证可移植性,上述自定义类应该仅用来尝试访问或更新缓存。

在实现和部署环境中,上述自定义类可以利用一些实用技术比如依赖注入来允许直接访问应用程序和部署专用资源,但是并不需要实现去支持这个功能。

可重入

尽管此规范不限制开发人员在使用自定义 EntryProcessorCacheEntryListenerCacheLoaderCacheWriterExpiryPolicy 时可能执行的操作,但缓存实现可能会限制这些接口的重入。 例如; 一个实现可能会限制 EntryProcessor 调用 Cache 上的方法或调用其他 EntryProcessor 的能力。 同样,实现可能会限制 CacheLoader / CacheWriter 访问缓存的能力。

因此,强烈建议开发人员避免编写这些接口的可重入实现,因为这些实现可能不是可移植的。

一个简单的例子

String.class,Interger.class

这个简单例子穿件一个默认的 CacheManager ,配置一个名叫 "simpleCache" 的缓存,使用 String 做 key, Integer 为 value,一个小时无访问过期,然后做了一些缓存操作

//resolve a cache manager
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

//configure the cache
MutableConfiguration<String, Integer> config =
new MutableConfiguration<>()
    .setTypes(String.class, Integer.class)
    .setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(ONE_HOUR))
    .setStatisticsEnabled(true);

//create the cache
Cache<String, Integer> cache = cacheManager.createCache("simpleCache", config);

//cache operations
String key = "key";
Integer value1 = 1;
cache.put("key", value1);
Integer value2 = cache.get(key);
assertEquals(value1, value2);
cache.remove(key);
assertNull(cache.get(key));

在上面的例子中, CacheingProviderCacheManager 使用的都是默认的实现。除了上面的获取缓存的方法以外,还有下面一种

//get the cache
Cache<String, Integer> cache = Caching.getCache("simpleCache",
    String.class, Integer.class);

CacheManager

CacheManeger 是 Java caching API 的核心概念。开发者通过 CacheManagerCache 交互。

CacheManager 提供以下能力:

  • 创建和配置有唯一名称的 Cache
  • 通过缓存名获取 Cache
  • 限定 Cache 的命名空间, 具有相同名称但源自不同缓存管理器的缓存被视为不同缓存。
  • 当不再需要缓存管理的时候关闭缓存。
  • 注销 Cache 和其中缓存的数据。
  • 如果需要, Cache 将使用 ClassLoader 加载所需的应用类。
  • 迭代当前被管理的 Cache
  • 关闭 CacheManager 和其管理的 Cache
  • 启用和禁用 Cache 统计数据收集。
  • 启用和禁用 Cache 的 JMX 管理。
  • 获取 CachingProviderCacheManager 定义的属性。
  • 查询 CachingProvider 提供的能力和可选特性。

CacheManager 的接口定义:

/**
 * A {@link CacheManager} provides a means of establishing, configuring,
 * acquiring, closing and destroying uniquely named {@link Cache}s.
 * <p>
 * {@link Cache}s produced and owned by a {@link CacheManager} typically share
 * common infrastructure, for example, a common {@link ClassLoader} and
 * implementation specific {@link Properties}.
 * <p>
 * Implementations of {@link CacheManager} may additionally provide and share
 * external resources between the {@link Cache}s being managed, for example,
 * the content of the managed {@link Cache}s may be stored in the same cluster.
 * <p>
 * By default {@link CacheManager} instances are typically acquired through the
 * use of a {@link CachingProvider}.  Implementations however may additionally
 * provide other mechanisms to create, acquire, manage and configure
 * {@link CacheManager}s, including:
 * <ul>
 * <li>making use of {@link java.util.ServiceLoader}s,</li>
 * <li>permitting the use of the <code>new</code> operator to create a
 * concrete implementation, </li>
 * <li>providing the construction through the use of one or more
 * builders, and</li>
 * <li>through the use of dependency injection.</li>
 * </ul>
 * <p>
 * The default {@link CacheManager} however can always be acquired using the
 * default configured {@link CachingProvider} obtained by the {@link Caching}
 * class.  For example:
 * <pre><code>
 * CachingProvider provider = Caching.getCachingProvider();
 * CacheManager manager = provider.getCacheManager();
 * </code></pre>
 * <p>
 * Within a Java process {@link CacheManager}s and the {@link Cache}s they
 * manage are scoped and uniquely identified by a {@link URI},  the meaning of
 * which is implementation specific. To obtain the default {@link URI},
 * {@link ClassLoader} and {@link Properties} for an implementation, consult the
 * {@link CachingProvider} class.
 *
 *
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @author Brian Oliver
 * @see Caching
 * @see CachingProvider
 * @see Cache
 * @since 1.0
 *
 */
public interface CacheManager extends Closeable {

  /**
   * Get the {@link CachingProvider} that created and is responsible for
   * the {@link CacheManager}.
   *
   * @return the CachingProvider or <code>null</code> if the {@link CacheManager}
   *         was created without using a {@link CachingProvider}
   */
  CachingProvider getCachingProvider();

  /**
   * Get the URI of the {@link CacheManager}.
   *
   * @return the URI of the {@link CacheManager}
   */
  URI getURI();

  /**
   * Get the {@link ClassLoader} used by the {@link CacheManager}.
   *
   * @return  the {@link ClassLoader} used by the {@link CacheManager}
   */
  ClassLoader getClassLoader();

  /**
   * Get the {@link Properties} that were used to create this
   * {@link CacheManager}.
   * <p>
   * Implementations are not required to re-configure the
   * {@link CacheManager} should modifications to the returned
   * {@link Properties} be made.
   *
   * @return the Properties used to create the {@link CacheManager}
   */
  Properties getProperties();

  /**
   * Creates a named {@link Cache} at runtime.
   * <p>
   * If a {@link Cache} with the specified name is known to the {@link
   * CacheManager}, a CacheException is thrown.
   * <p>
   * If a {@link Cache} with the specified name is unknown the {@link
   * CacheManager}, one is created according to the provided {@link Configuration}
   * after which it becomes managed by the {@link CacheManager}.
   * <p>
   * Prior to a {@link Cache} being created, the provided {@link Configuration}s is
   * validated within the context of the {@link CacheManager} properties and
   * implementation.
   * <p>
   * Implementers should be aware that the {@link Configuration} may be used to
   * configure other {@link Cache}s.
   * <p>
   * There's no requirement on the part of a developer to call this method for
   * each {@link Cache} an application may use.  Implementations may support
   * the use of declarative mechanisms to pre-configure {@link Cache}s, thus
   * removing the requirement to configure them in an application.  In such
   * circumstances a developer may simply call either the
   * {@link #getCache(String)} or {@link #getCache(String, Class, Class)}
   * methods to acquire a previously established or pre-configured {@link Cache}.
   *
   * @param <K> the type of key
   * @param <V> the type of value
   * @param <C> the type of the Configuration
   * @param cacheName     the name of the {@link Cache}. Names should not use
   *                      forward slashes(/) or colons(:), or start with
   *                      java. or javax. These prefixes are reserved.
   * @param configuration a {@link Configuration} for the {@link Cache}
   * @throws IllegalStateException         if the {@link CacheManager}
   *                                       {@link #isClosed()}
   * @throws CacheException                if there was an error configuring the
   *                                       {@link Cache}, which includes trying
   *                                       to create a cache that already exists.
   * @throws IllegalArgumentException      if the configuration is invalid
   * @throws UnsupportedOperationException if the configuration specifies
   *                                       an unsupported feature
   * @throws NullPointerException          if the cache configuration or name
   *                                       is null
   * @throws SecurityException             when the operation could not be performed
   *                                       due to the current security settings
   */
  <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(String cacheName,
                                                                C configuration)
      throws IllegalArgumentException;


  /**
   * Looks up a managed {@link Cache} given its name.
   * <p>
   * Use this method to check runtime key and value types.
   * <p>
   * Use {@link #getCache(String)} where this check is not required.
   * <p>
   * Implementations must ensure that the key and value types are the same as
   * those configured for the {@link Cache} prior to returning from this method.
   * <p>
   * Implementations may further perform type checking on mutative cache operations
   * and throw a {@link ClassCastException} if these checks fail.
   * <p>
   * Implementations that support declarative mechanisms for pre-configuring
   * {@link Cache}s may return a pre-configured {@link Cache} instead of
   * <code>null</code>.
   *
   * @param <K> the type of key
   * @param <V> the type of value
   * @param cacheName the name of the managed {@link Cache} to acquire
   * @param keyType   the expected {@link Class} of the key
   * @param valueType the expected {@link Class} of the value
   * @return the Cache or null if it does exist or can't be pre-configured
   * @throws IllegalStateException    if the {@link CacheManager}
   *                                  is {@link #isClosed()}
   * @throws ClassCastException       if the specified key and/or value types are
   *                                  incompatible with the configured cache.
   * @throws NullPointerException     if either keyType or classType is null.
   * @throws SecurityException        when the operation could not be performed
   *                                  due to the current security settings
   */
  <K, V> Cache<K, V> getCache(String cacheName, Class<K> keyType,
                              Class<V> valueType);

  /**
   * Looks up a managed {@link Cache} given its name.
   * <p>
   * This method may only be used to acquire {@link Cache}s that were
   * configured without runtime key and value types, or were configured
   * to use Object.class key and value types.
   * <p>
   * Use the {@link #getCache(String, Class, Class)} method to acquire
   * {@link Cache}s with a check that the supplied key and value type parameters
   * match the runtime types.
   * <p>
   * Implementations that support declarative mechanisms for pre-configuring
   * {@link Cache}s may return a pre-configured {@link Cache} instead of
   * <code>null</code>.
   *
   * @param <K> the type of key
   * @param <V> the type of value
   * @param cacheName the name of the cache to look for
   * @return the Cache or null if it does exist or can't be pre-configured
   * @throws IllegalStateException    if the CacheManager is {@link #isClosed()}
   * @throws SecurityException        when the operation could not be performed
   *                                  due to the current security settings
   * @see #getCache(String, Class, Class)
   */
  <K, V> Cache<K, V> getCache(String cacheName);

  /**
   * Obtains an {@link Iterable} over the names of {@link Cache}s managed by the
   * {@link CacheManager}.
   * <p>
   * {@link java.util.Iterator}s returned by the {@link Iterable} are immutable.
   * If the {@link Cache}s managed by the {@link CacheManager} change,
   * the {@link Iterable} and associated {@link java.util.Iterator}s are not
   * affected.
   * <p>
   * {@link java.util.Iterator}s returned by the {@link Iterable} may not provide
   * all of the {@link Cache}s managed by the {@link CacheManager}.  For example:
   * Internally defined or platform specific {@link Cache}s that may be accessible
   * by a call to {@link #getCache(String)} or {@link #getCache(String, Class,
   * Class)} may not be present in an iteration.
   *
   * @return an {@link Iterable} over the names of managed {@link Cache}s.
   * @throws IllegalStateException if the {@link CacheManager}
   *                               is {@link #isClosed()}
   * @throws SecurityException     when the operation could not be performed
   *                               due to the current security settings
   */
  Iterable<String> getCacheNames();

  /**
   * Destroys a specifically named and managed {@link Cache}.  Once destroyed
   * a new {@link Cache} of the same name but with a different {@link
   * Configuration} may be configured.
   * <p>
   * This is equivalent to the following sequence of method calls:
   * <ol>
   * <li>{@link Cache#clear()}</li>
   * <li>{@link Cache#close()}</li>
   * </ol>
   * followed by allowing the name of the {@link Cache} to be used for other
   * {@link Cache} configurations.
   * <p>
   * From the time this method is called, the specified {@link Cache} is not
   * available for operational use. An attempt to call an operational method on
   * the {@link Cache} will throw an {@link IllegalStateException}.
   *
   * @param cacheName the cache to destroy
   * @throws IllegalStateException if the {@link CacheManager}
   *                               {@link #isClosed()}
   * @throws NullPointerException  if cacheName is null
   * @throws SecurityException     when the operation could not be performed
   *                               due to the current security settings
   */
  void destroyCache(String cacheName);

  /**
   * Controls whether management is enabled. If enabled the {@link CacheMXBean}
   * for each cache is registered in the platform MBean server. The platform
   * MBeanServer is obtained using
   * {@link ManagementFactory#getPlatformMBeanServer()}.
   * <p>
   * Management information includes the name and configuration information for
   * the cache.
   * <p>
   * Each cache's management object must be registered with an ObjectName that
   * is unique and has the following type and attributes:
   * <p>
   * Type:
   * <code>javax.cache:type=CacheConfiguration</code>
   * <p>
   * Required Attributes:
   * <ul>
   * <li>CacheManager the URI of the CacheManager
   * <li>Cache the name of the Cache
   * </ul>
   *
   * @param cacheName the name of the cache to register
   * @param enabled   true to enable management, false to disable.
   * @throws IllegalStateException if the {@link CacheManager} or
   *                               {@link Cache} {@link #isClosed()}
   * @throws SecurityException     when the operation could not be performed
   *                               due to the current security settings
   */
  void enableManagement(String cacheName, boolean enabled);

  /**
   * Enables or disables statistics gathering for a managed {@link Cache} at
   * runtime.
   * <p>
   * Each cache's statistics object must be registered with an ObjectName that
   * is unique and has the following type and attributes:
   * <p>
   * Type:
   * <code>javax.cache:type=CacheStatistics</code>
   * <p>
   * Required Attributes:
   * <ul>
   * <li>CacheManager the URI of the CacheManager
   * <li>Cache the name of the Cache
   * </ul>
   *
   * @param cacheName the name of the cache to register
   * @param enabled   true to enable statistics, false to disable.
   * @throws IllegalStateException if the {@link CacheManager} or
   *                               {@link Cache} {@link #isClosed()}
   * @throws NullPointerException  if cacheName is null
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  void enableStatistics(String cacheName, boolean enabled);

  /**
   * Closes the {@link CacheManager}.
   * <p>
   * For each {@link Cache} managed by the {@link CacheManager}, the
   * {@link Cache#close()} method will be invoked, in no guaranteed order.
   * <p>
   * If a {@link Cache#close()} call throws an exception, the exception will be
   * ignored.
   * <p>
   * After executing this method, the {@link #isClosed()} method will return
   * <code>true</code>.
   * <p>
   * All attempts to close a previously closed {@link CacheManager} will be
   * ignored.
   *
   * Closing a CacheManager does not necessarily destroy the contents of the
   * Caches in the CacheManager.
   * <p>
   * It simply signals that the CacheManager is no longer required by the application
   * and that future uses of a specific CacheManager instance should not be permitted.
   * <p>
   * Depending on the implementation and Cache topology,
   * (e.g. a storage-backed or distributed cache), the contents of closed Caches
   * previously referenced by the CacheManager, may still be available and accessible
   * by other applications.
   *
   * @throws SecurityException when the operation could not be performed due to the
   *         current security settings
   */
  void close();

  /**
   * Determines whether the {@link CacheManager} instance has been closed. A
   * {@link CacheManager} is considered closed if;
   * <ol>
   * <li>the {@link #close()} method has been called</li>
   * <li>the associated {@link #getCachingProvider()} has been closed, or</li>
   * <li>the {@link CacheManager} has been closed using the associated
   * {@link #getCachingProvider()}</li>
   * </ol>
   * <p>
   * This method generally cannot be called to determine whether the
   * {@link CacheManager} is valid or invalid. A typical client can determine
   * that a {@link CacheManager} is invalid by catching any exceptions that
   * might be thrown when an operation is attempted.
   *
   * @return true if this {@link CacheManager} instance is closed; false if it
   *         is still open
   */
  boolean isClosed();

  /**
   * Provides a standard mechanism to access the underlying concrete caching
   * implementation to provide access to further, proprietary features.
   * <p>
   * If the provider's implementation does not support the specified class,
   * the {@link IllegalArgumentException} is thrown.
   *
   * @param <T> the type of the underlying {@link CacheManager}
   * @param clazz the proprietary class or interface of the underlying concrete
   *              {@link CacheManager}. It is this type that is returned.
   * @return an instance of the underlying concrete {@link CacheManager}
   * @throws IllegalArgumentException if the caching provider doesn't support the
   *                                  specified class.
   * @throws SecurityException        when the operation could not be performed
   *                                  due to the current security settings
   */
  <T> T unwrap(java.lang.Class<T> clazz);
}

获取默认 CacheManager

为了简化 API,开发者可以使用 Caching 类提供的默认 CachingProvider 类,获取默认的 CacheManager 。比如

// acquire the default CachingProvider
CachingProvider provider = Caching.getCachingProvider();

// acquire the default CacheManager
CacheManager manager = provider.getCacheManager();

为了获取非默认或者说自定义的 CacheManager ,需要使用 CachingProvider 提供的重载版本的 getCacheManager 方法。

如何配置一个 CachingProvider 会在后面的一节单独介绍。

配置 Cache

CacheManager 配置缓存有两种方式。

  • CacheManager 必须允许应用程序在运行时编程式的配置缓存,通过 <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(String cacheName, C configuration) 方法
  • CacheManager 可以有选择的提供声明式配置来防止在应用程序中使用 createCache 方法。

对于缓存实现来说, CacheManager 如何实现声明式的配置是自由的,其中一种是通过 XML 文件来配置 CacheManager 和其中的缓存。

配置相关的类在 javax.cache.configuration 包下。里面提供了最小化配置( Configuration )和完整配置( CompleteConfiguration )的接口。

尽管此规范提供了一个最小化配置,但是只有完全支持 CompleteConfiguration 接口的实现才符合该规范。

javax.cache.configuration.Configuration 接口定义如下:

package javax.cache.configuration;

import javax.cache.Cache;
import javax.cache.CacheManager;
import java.io.Serializable;

/**
 * A basic read-only representation of a {@link Cache} configuration.
 * <p>
 * The properties provided by instances of this interface are used by
 * {@link CacheManager}s to configure {@link Cache}s.
 * <p>
 * Implementations of this interface must override {@link Object#hashCode()} and
 * {@link Object#equals(Object)} as {@link Configuration}s are often compared at
 * runtime.
 *
 * @param <K> the type of keys maintained the cache
 * @param <V> the type of cached values
 * @author Greg Luck
 * @author Brian Oliver
 * @since 1.0
 */
public interface Configuration<K, V> extends Serializable {

  /**
   * Determines the required type of keys for {@link Cache}s configured
   * with this {@link Configuration}.
   *
   * @return the key type or <code>Object.class</code> if the type is undefined
   */
  Class<K> getKeyType();

  /**
   * Determines the required type of values for {@link Cache}s configured
   * with this {@link Configuration}.
   *
   * @return the value type or <code>Object.class</code> if the type is undefined
   */
  Class<V> getValueType();

  /**
   * Whether storeByValue (true) or storeByReference (false).
   * When true, both keys and values are stored by value.
   * <p>
   * When false, both keys and values are stored by reference.
   * Caches stored by reference are capable of mutation by any threads holding
   * the reference. The effects are:
   * <ul>
   * <li>if the key is mutated, then the key may not be retrievable or
   * removable</li>
   * <li>if the value is mutated, then all threads in the JVM can potentially
   * observe those mutations, subject to the normal Java Memory Model rules.</li>
   * </ul>
   * Storage by reference only applies to the local heap. If an entry is moved off
   * heap it will need to be transformed into a representation. Any mutations that
   * occur after transformation may not be reflected in the cache.
   * <p>
   * When a cache is storeByValue, any mutation to the key or value does not
   * affect the key of value stored in the cache.
   * <p>
   * The default value is <code>true</code>.
   *
   * @return true if the cache is store by value
   */
  boolean isStoreByValue();
}

javax.cache.configuration.CompleteConfiguration 接口定义如下:

package javax.cache.configuration;

import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheWriter;
import java.io.Serializable;

/**
 * A read-only representation of the complete JCache {@link javax.cache.Cache}
 * configuration.
 * <p>
 * The properties provided by instances of this interface are used by
 * {@link javax.cache.CacheManager}s to configure {@link javax.cache.Cache}s.
 * <p>
 * Implementations of this interface must override {@link Object#hashCode()} and
 * {@link Object#equals(Object)} as
 * {@link javax.cache.configuration.CompleteConfiguration}s are often compared at
 * runtime.
 *
 * @param <K> the type of keys maintained the cache
 * @param <V> the type of cached values
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @author Brian Oliver
 * @since 1.0
 */
public interface CompleteConfiguration<K, V> extends Configuration<K, V>,
    Serializable {

  /**
   * Determines if a {@link javax.cache.Cache} should operate in read-through mode.
   * <p>
   * When in "read-through" mode, cache misses that occur due to cache entries
   * not existing as a result of performing a "get" will appropriately
   * cause the configured {@link javax.cache.integration.CacheLoader} to be
   * invoked.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return <code>true</code> when a {@link javax.cache.Cache} is in
   * "read-through" mode.
   * @see #getCacheLoaderFactory()
   */
  boolean isReadThrough();

  /**
   * Determines if a {@link javax.cache.Cache} should operate in write-through
   * mode.
   * <p>
   * When in "write-through" mode, cache updates that occur as a result of
   * performing "put" operations called via one of
   * {@link javax.cache.Cache#put(Object, Object)},
   * {@link javax.cache.Cache#getAndRemove(Object)},
   * {@link javax.cache.Cache#removeAll()},
   * {@link javax.cache.Cache#getAndPut(Object, Object)}
   * {@link javax.cache.Cache#getAndRemove(Object)},
   * {@link javax.cache.Cache#getAndReplace(Object,
   * Object)}, {@link javax.cache.Cache#invoke(Object,
   * javax.cache.processor.EntryProcessor,
   * Object...)}, {@link javax.cache.Cache#invokeAll(java.util.Set,
   * javax.cache.processor.EntryProcessor, Object...)} will appropriately cause
   * the configured {@link javax.cache.integration.CacheWriter} to be invoked.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return <code>true</code> when a {@link javax.cache.Cache} is in
   *        "write-through" mode.
   * @see #getCacheWriterFactory()
   */
  boolean isWriteThrough();

  /**
   * Checks whether statistics collection is enabled in this cache.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return true if statistics collection is enabled
   */
  boolean isStatisticsEnabled();

  /**
   * Checks whether management is enabled on this cache.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return true if management is enabled
   */
  boolean isManagementEnabled();

  /**
   * Obtains the {@link javax.cache.configuration.CacheEntryListenerConfiguration}s
   * for {@link javax.cache.event.CacheEntryListener}s to be configured on a
   * {@link javax.cache.Cache}.
   *
   * @return an {@link Iterable} over the
   * {@link javax.cache.configuration.CacheEntryListenerConfiguration}s
   */
  Iterable<CacheEntryListenerConfiguration<K,
      V>> getCacheEntryListenerConfigurations();

  /**
   * Gets the {@link javax.cache.configuration.Factory} for the
   * {@link javax.cache.integration.CacheLoader}, if any.
   * <p>
   * A CacheLoader should be configured for "Read Through" caches to load values
   * when a cache miss occurs using either the
   * {@link javax.cache.Cache#get(Object)} and/or
   * {@link javax.cache.Cache#getAll(java.util.Set)} methods.
   * <p>
   * The default value is <code>null</code>.
   *
   * @return the {@link javax.cache.configuration.Factory} for the
   * {@link javax.cache.integration.CacheLoader} or null if none has been set.
   */
  Factory<CacheLoader<K, V>> getCacheLoaderFactory();

  /**
   * Gets the {@link javax.cache.configuration.Factory} for the
   * {@link javax.cache.integration.CacheWriter}, if any.
   * <p>
   * The default value is <code>null</code>.
   *
   * @return the {@link javax.cache.configuration.Factory} for the
   * {@link javax.cache.integration.CacheWriter} or null if none has been set.
   */
  Factory<CacheWriter<? super K, ? super V>> getCacheWriterFactory();

  /**
   * Gets the {@link javax.cache.configuration.Factory} for the
   * {@link javax.cache.expiry.ExpiryPolicy} to be used for caches.
   * <p>
   * The default value is a {@link javax.cache.configuration.Factory} that will
   * produce a {@link javax.cache.expiry.EternalExpiryPolicy} instance.
   *
   * @return the {@link javax.cache.configuration.Factory} for
   * {@link javax.cache.expiry.ExpiryPolicy} (must not be <code>null</code>)
   */
  Factory<ExpiryPolicy> getExpiryPolicyFactory();

}

为了方便配置,提供了一个 javax.cache.configuration.MutableConfiguration 作为 CompleteConfiguration 的实现类。

缓存实现可以提供自定义的 Configuration 接口实现来拓展配置。

为了简化 MutableConfiguration 的使用,所有 setter 方法都返回 MutableConfiguration 的实例。

CacheManger 有责任去校验 Cache 的配置是否合法, Cache 配置可以在创建时被 CacheManager 接受,在创建 Cache 时抛出 IllegalArgumentException

MutableConfiguration 经常使用的构造器和和 setter 方法如下:

/**
   * Default JavaBean constructor.
   * <p>
   * Creates a default configuration. Default configurations have no
   * runtime type checking and are set for eternal expiry.
   * </p><p>
   * To enable runtime type enforcement, if supported by the implementation, call
   * {@link #setTypes} after construction.
   * </p><p>
   * After construction set any other configuration parameters in the
   * fluent style. e.g.
   * </p>
   * <pre>{@code
   * CacheConfiguration<Integer, String> = new MutableConfiguration<Integer, String>()
   *         .setTypes(Integer.class, String.class)
   *         .setReadThrough(true)
   *         . . .
   * }</pre>
   * @see #setTypes(Class, Class)
   */
  public MutableConfiguration()

  /**
   * Constructs a {@link MutableConfiguration} based on another
   * {@link CompleteConfiguration}.
   *
   * @param configuration the {@link CompleteConfiguration}
   */
  public MutableConfiguration(CompleteConfiguration<K, V> configuration)

  /**
   * Sets the expected type of keys and values for a {@link Cache}
   * configured with this {@link Configuration}. Setting both to
   * <code>Object.class</code> means type-safety checks are not required.
* <p>
   * This is used by {@link CacheManager} to ensure that the key and value
   * types are the same as those configured for the {@link Cache} prior to
   * returning a requested cache from this method.
   * <p>
   * Implementations may further perform type checking on mutative cache operations
   * and throw a {@link ClassCastException} if these checks fail.
   *
   * @param keyType   the expected key type
   * @param valueType the expected value type
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   * @throws NullPointerException should the key or value type be null
   * @see CacheManager#getCache(String, Class, Class)
   */
  public MutableConfiguration<K, V> setTypes(Class<K> keyType, Class<V> valueType)


  /**
   * Add a configuration for a {@link CacheEntryListener}.
   *
   * @param cacheEntryListenerConfiguration the
   *  {@link CacheEntryListenerConfiguration}
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   * @throws IllegalArgumentException is the same CacheEntryListenerConfiguration
   * is used more than once
   */
  public MutableConfiguration<K, V> addCacheEntryListenerConfiguration(
      CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)


  /**
   * Remove a configuration for a {@link CacheEntryListener}.
   *
   * @param cacheEntryListenerConfiguration  the
   *     {@link CacheEntryListenerConfiguration} to remove
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> removeCacheEntryListenerConfiguration(
      CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)


  /**
   * Set the {@link CacheLoader} factory.
   *
   * @param factory the {@link CacheLoader} {@link Factory}
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setCacheLoaderFactory(Factory<? extends
      CacheLoader<K, V>> factory)

  /**
   * Set the {@link CacheWriter} factory.
   *
 * @param factory the {@link CacheWriter} {@link Factory}
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setCacheWriterFactory(Factory<? extends
      CacheWriter<? super K, ? super V>> factory)

  /**
   * Set the {@link Factory} for the {@link ExpiryPolicy}.  If <code>null</code>
   * is specified the default {@link ExpiryPolicy} is used.
   * <p>
   * Only one expiry policy can be set for a cache. The last policy applied
   * before cache construction will be the one used.
   * @param factory the {@link ExpiryPolicy} {@link Factory}
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setExpiryPolicyFactory(Factory<? extends
      ExpiryPolicy> factory)

  /**
   * Set if read-through caching should be used.
   * <p>
   * It is an invalid configuration to set this to true without specifying a
   * {@link CacheLoader} {@link Factory}.
   *
   * @param isReadThrough <code>true</code> if read-through is required
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setReadThrough(boolean isReadThrough)


  /**
   * Set if write-through caching should be used.
   * <p>
   * It is an invalid configuration to set this to true without specifying a
   * {@link CacheWriter} {@link Factory}.
   *
   * @param isWriteThrough <code>true</code> if write-through is required
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setWriteThrough(boolean isWriteThrough)


  /**
   * Set if a configured cache should use store-by-value or store-by-reference
   * semantics.
   *
   * @param isStoreByValue <code>true</code> if store-by-value is required,
   *                       <code>false</code> for store-by-reference
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setStoreByValue(boolean isStoreByValue)

  /**
   * Sets whether statistics gathering is enabled on a cache.
   * <p>
   * Statistics may be enabled or disabled at runtime via
   * {@link CacheManager#enableStatistics(String, boolean)}.
   *
   * @param enabled true to enable statistics, false to disable.
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setStatisticsEnabled(boolean enabled)

  /**
   * Sets whether management is enabled on a cache.
   * <p>
   * Management may be enabled or disabled at runtime via
   * {@link CacheManager#enableManagement(String, boolean)}.
   *
   * @param enabled true to enable statistics, false to disable.
   * @return the {@link MutableConfiguration} to permit fluent-style method calls
   */
  public MutableConfiguration<K, V> setManagementEnabled(boolean enabled)

MutableConfiguration 中提供的配置和 Configuration 会在后面的章节中深入讨论。

下表是一个 MutableConfiguration 实例所提供的默认值。

配置项 类型 默认值
Key 类型 Class<?> Object.class
Value 类型 Class<?> Object.class
Cache Loader Factory Factory<CacheLoader<K, V>> null
Cache Writer Factory Factory<CacheWriter<? super K, ? super V>> null
Expiry Policy Factory Factory<ExpiryPolicy<K>> a factory producing an EternalExpiryPolicy
Read Through Enabled boolean false
Write Through Enabled boolean false
Cache Entry Listener Configuration Iterable<CacheEntryListenerConfiguration<? super K, ? super V>> an empty iteration
Statistics Enabled boolean false
Management Enabled boolean false

缓存名和缓存作用域

对于每一个 Cache ,在创建该 CacheCacheManager 的作用域内,由 缓存名 唯一确定

因为缓存名作为 Java String 存在,所以就可移植性来说,有如下限制和推荐的命名规定

  • java.javax. 开头的缓存名不能使用。因为可能作为平台级的缓存名前缀。
  • 缓存名不能包含 /: ,因为可能被用于 Java EE 环境中基于 JNDI 查询。
  • 缓存名可以使用 Unicode 字符

虽然不是必须的,应用程序恶意使用要缓存的 Value 类的全类名作为缓存名,比如 缓存 Orders 的缓存可以叫 com.mycompany.Orders

获取缓存

目前有两种方式从 CacheManager 中获取缓存。

  • 当需要一个类型安全的 Cache 时,应该使用 CacheManger 提供的 <K, V> Cache<K, V> getCache(String cacheName,Class<K> keyType, Class<V> valueType)
  • 当不需要类型安全校验时(由应用自己来确保),应该使用 <K, V> Cache<K, V> Cache getCache(String cacheName);

可以查阅 缓存类型安全 一节来获得相关的更多信息。

一个简单的获取缓存的例子:

Cache<String, Integer> cache = cacheManager.getCache(
        "simpleCache", String.class, Integer.class);

CacheCacheManager 的生命周期

所有 CacheCacheManager 实例只有两个状态, openedclosed ,当打开时,所有操作都是允许的,包括创建、更新、删除条目或配置、获取、关闭、删除缓存等。当关闭时,所有操作都会抛出 IllegalStateException

关闭缓存

通过调用 Cache.close() 方法关闭 Cache 会向创建或拥有该 CacheCacheManager 发出信号,表明不应再对其进行管理。在次数, CacheManger 做了如下操作:

  • 必须关闭和释放与该缓存相关的所有资源。这包括调用 CacheLoaderCacheWriterCacheEntryListenerExpiryPolicy 实例的 close 方法。
  • 不再传递事件给该 CacheCacheEntryListener
  • 当调用 CacheManager getCacheNames() 时,不再返回该缓存的名字。

一旦缓存关闭后,所有对缓存的操作都会抛出 IllegalStateException ,以下是操作方法:

- clear
- containsKey
- deregisterCacheEntryListener
- get
- getAll
- getAndPut
- getAndRemove
- getAndReplace
- invoke
- invokeAll
- iterator
- loadAll
- put
- putAll
- putIfAbsent
- registerCacheEntryListener
- remove
- removeAll
- replace

关闭缓存并不一定必须要销毁缓存的所有内容。它仅仅是向拥有它的 CacheManager 发出信号。表示应用程序已经不再需要该缓存且不允许对该缓存做任何操作。根据实现的不同(基于存储或分布式),已关闭的缓存内容可能仍然可以被【其他】应用程序访问,不管是在关闭之前获取到的还是在关闭之后获取到的。

销毁缓存

要销毁一个 Cache ,将其从被管理的状态中释放出来,并删除所有缓存条目,从而允许创建一个具有相同名称但可能具有不同配置的新缓存,应调用 CacheManager destroyCache 方法。

/**
 * Destroys a specifically named and managed {@link Cache}.  Once destroyed
 * a new {@link Cache} of the same name but with a different {@link
 * Configuration} may be configured.
 * <p>
 * This is equivalent to the following sequence of method calls:
 * <ol>
 * <li>{@link Cache#clear()}</li>
 * <li>{@link Cache#close()}</li>
 * </ol>
 * followed by allowing the name of the {@link Cache} to be used for other
 * {@link Cache} configurations.
 * <p>
 * From the time this method is called, the specified {@link Cache} is not
 * available for operational use. An attempt to call an operational method on
 * the {@link Cache} will throw an {@link IllegalStateException}.
 *
 * @param cacheName the cache to destroy
 * @throws IllegalStateException if the {@link CacheManager}
 *                               {@link #isClosed()}
 * @throws NullPointerException  if cacheName is null
 * @throws SecurityException     when the operation could not be performed
 *                               due to the current security settings
 */
void destroyCache(String cacheName);

一旦被销毁:

IllegalStateException

一旦销毁,就无法通过 CacheManager 使用缓存。 销毁缓存可确保关闭缓存,并且无论应用程序或拓扑如何,所有应用程序都将立即或将来不再使用所有关联条目。

关闭 CacheManager

可以通过 CacheManager.close() 或者 CachingProvider.close(...) 方法来关闭 CacheManager ,这两个方法调用会有如下效果:

  • 关闭被其管理的所有 Cache
  • 释放用来管理 Cache 的所有资源。
  • 一旦关闭,任何试图操作 CacheManager 或其管理的 Cache 的行为都将抛出 IllegalStateException ,其中涉及的方法如下:
- createCache
- destroyCache
- enableManagement
- enableStatistics
- getCache
- getCacheNames

关闭 CacheManager 后,可以使用最初创建 CacheManagerCachingProvider 创建另外一个实例。这部分将在 CachingProvder 部分进行介绍。

关闭 CacheManager 并不一定会销毁 CacheManagerCache 里的数据。 它只是表明应用程序不再需要拥有的 CacheManager ,并且不应再允许该特定 CacheManager 实例的将来使用。 取决于实现(基于存储或分布式),已关闭的 CacheManager 管理的 Cache 的数据可能仍然可以被【其他】应用程序访问。

类加载

同一个 CacheManager 中的所有 Cache 共用用于创建 CacheManager 的类加载器。

如果不同的 Cache 要使用不同的类加载器,则必须通过创建不同的 CacheManager 实现。有关如何配置 CacheManager 的信息,请参阅关于 CachingProvider 的部分。

Cache

开发者主要通过 javax.cache.CacheCache 进行交互。

javax.cache.Cache 接口提供了 Map-like 的方法来访问、更新、删除缓存条目。

javax.cache.Cache 接口定义如下:

import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.event.CacheEntryListener;
import javax.cache.event.CacheEntryRemovedListener;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheWriter;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import java.io.Closeable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A {@link Cache} is a Map-like data structure that provides temporary storage
 * of application data.
 * <p>
 * Like {@link Map}s, {@link Cache}s
 * <ol>
 * <li>store key-value pairs, each referred to as an {@link Entry}</li>
 * <li>allow use of Java Generics to improve application type-safety</li>
 * <li>are {@link Iterable}</li>
 * </ol>
 * <p>
 * Unlike {@link Map}s, {@link Cache}s
 * <ol>
 * <li>do not allow null keys or values.  Attempts to use <code>null</code>
 * will result in a {@link NullPointerException}</li>
 * <li>provide the ability to read values from a
 * {@link CacheLoader} (read-through-caching)
 * when a value being requested is not in a cache</li>
 * <li>provide the ability to write values to a
 * {@link CacheWriter} (write-through-caching)
 * when a value being created/updated/removed from a cache</li>
 * <li>provide the ability to observe cache entry changes</li>
 * <li>may capture and measure operational statistics</li>
 * </ol>
 * <p>
 * A simple example of how to use a cache is:
 * <pre><code>
 * String cacheName = "sampleCache";
 * CachingProvider provider = Caching.getCachingProvider();
 * CacheManager manager = provider.getCacheManager();
 * Cache<Integer, Date> cache = manager.getCache(cacheName, Integer.class,
 *                                                     Date.class);
 * Date value1 = new Date();
 * Integer key = 1;
 * cache.put(key, value1);
 * Date value2 = cache.get(key);
 * </code></pre>
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @author Brian Oliver
 * @since 1.0
 */
public interface Cache<K, V> extends Iterable<Cache.Entry<K, V>>, Closeable {
    /**
     * Gets an entry from the cache.
     * <p>
     * If the cache is configured to use read-through, and get would return null
     * because the entry is missing from the cache, the Cache's {@link CacheLoader}
     * is called in an attempt to load the entry.
     *
     * @param key the key whose associated value is to be returned
     * @return the element, or null, if it does not exist.
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws NullPointerException  if the key is null
     * @throws CacheException        if there is a problem fetching the value
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     */
    V get(K key);

    /**
     * Gets a collection of entries from the {@link Cache}, returning them as
     * {@link Map} of the values associated with the set of keys requested.
     * <p>
     * If the cache is configured read-through, and a get for a key would
     * return null because an entry is missing from the cache, the Cache's
     * {@link CacheLoader} is called in an attempt to load the entry. If an
     * entry cannot be loaded for a given key, the key will not be present in
     * the returned Map.
     *
     * @param keys The keys whose associated values are to be returned.
     * @return A map of entries that were found for the given keys. Keys not found
     *         in the cache are not in the returned map.
     * @throws NullPointerException  if keys is null or if keys contains a null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem fetching the values
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     */
    Map<K, V> getAll(Set<? extends K> keys);

    /**
     * Determines if the {@link Cache} contains an entry for the specified key.
     * <p>
     * More formally, returns <tt>true</tt> if and only if this cache contains a
     * mapping for a key <tt>k</tt> such that <tt>key.equals(k)</tt>.
     * (There can be at most one such mapping.)</p>
     * <p>
     * If the cache is configured read-through the associated {@link CacheLoader}
     * is not called. Only the cache is checked.
     * </p>
     * @param key key whose presence in this cache is to be tested.
     * @return <tt>true</tt> if this map contains a mapping for the specified key
     * @throws NullPointerException  if key is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        it there is a problem checking the mapping
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see java.util.Map#containsKey(Object)
     */
    boolean containsKey(K key);

    /**
     * Asynchronously loads the specified entries into the cache using the
     * configured {@link CacheLoader} for the given keys.
     * <p>
     * If an entry for a key already exists in the Cache, a value will be loaded
     * if and only if <code>replaceExistingValues</code> is true.   If no loader
     * is configured for the cache, no objects will be loaded.  If a problem is
     * encountered during the retrieving or loading of the objects,
     * an exception is provided to the {@link CompletionListener}.  Once the
     * operation has completed, the specified CompletionListener is notified.
     * <p>
     * Implementations may choose to load multiple keys from the provided
     * {@link Set} in parallel.  Iteration however must not occur in parallel,
     * thus allow for non-thread-safe {@link Set}s to be used.
     * <p>
     * The thread on which the completion listener is called is implementation
     * dependent. An implementation may also choose to serialize calls to
     * different CompletionListeners rather than use a thread per
     * CompletionListener.
     *
     * @param keys                  the keys to load
     * @param replaceExistingValues when true existing values in the Cache will
     *                              be replaced by those loaded from a CacheLoader
     * @param completionListener    the CompletionListener (may be null)
     * @throws NullPointerException  if keys is null or if keys contains a null.
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        thrown if there is a problem performing the
     *                               load. This may also be thrown on calling if
     *                               there are insufficient threads available to
     *                               perform the load.
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     */
    void loadAll(Set<? extends K> keys, boolean replaceExistingValues,
                 CompletionListener completionListener);

    /**
     * Associates the specified value with the specified key in the cache.
     * <p>
     * If the {@link Cache} previously contained a mapping for the key, the old
     * value is replaced by the specified value.  (A cache <tt>c</tt> is said to
     * contain a mapping for a key <tt>k</tt> if and only if {@link
     * #containsKey(Object) c.containsKey(k)} would return <tt>true</tt>.)
     * <p>
     * If the cache is configured write-through the
     * {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     *
     * @param key   key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @throws NullPointerException  if key is null or if value is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the put
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see java.util.Map#put(Object, Object)
     * @see #getAndPut(Object, Object)
     * @see #getAndReplace(Object, Object)
     * @see CacheWriter#write
     */
    void put(K key, V value);

    /**
     * Associates the specified value with the specified key in this cache,
     * returning an existing value if one existed.
     * <p>
     * If the cache previously contained a mapping for
     * the key, the old value is replaced by the specified value.  (A cache
     * <tt>c</tt> is said to contain a mapping for a key <tt>k</tt> if and only
     * if {@link #containsKey(Object) c.containsKey(k)} would return
     * <tt>true</tt>.)
     * <p>
     * The previous value is returned, or null if there was no value associated
     * with the key previously.</p>
     * <p>
     * If the cache is configured write-through the associated
     * {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     *
     * @param key   key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the value associated with the key at the start of the operation or
     *         null if none was associated
     * @throws NullPointerException  if key is null or if value is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the put
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see #put(Object, Object)
     * @see #getAndReplace(Object, Object)
     * @see CacheWriter#write(Cache.Entry)
     */
    V getAndPut(K key, V value);

    /**
     * Copies all of the entries from the specified map to the {@link Cache}.
     * <p>
     * The effect of this call is equivalent to that of calling
     * {@link #put(Object, Object) put(k, v)} on this cache once for each mapping
     * from key <tt>k</tt> to value <tt>v</tt> in the specified map.
     * <p>
     * The order in which the individual puts occur is undefined.
     * <p>
     * The behavior of this operation is undefined if entries in the cache
     * corresponding to entries in the map are modified or removed while this
     * operation is in progress. or if map is modified while the operation is in
     * progress.
     * <p>
     * In Default Consistency mode, individual puts occur atomically but not
     * the entire putAll.  Listeners may observe individual updates.
     * <p>
     * If the cache is configured write-through the associated
     * {@link CacheWriter#writeAll} method will be called.
     * </p>
     *
     * @param map mappings to be stored in this cache
     * @throws NullPointerException  if map is null or if map contains null keys
     *                               or values.
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the put.
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#writeAll
     */
    void putAll(java.util.Map<? extends K, ? extends V> map);

    /**
     * Atomically associates the specified key with the given value if it is
     * not already associated with a value.
     * <p>
     * This is equivalent to:
     * <pre><code>
     * if (!cache.containsKey(key)) {}
     *   cache.put(key, value);
     *   return true;
     * } else {
     *   return false;
     * }
     * </code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through, and this method returns true,
     * the associated {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     * @param key   key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return true if a value was set.
     * @throws NullPointerException  if key is null or value is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the put
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#write
     */
    boolean putIfAbsent(K key, V value);

    /**
     * Removes the mapping for a key from this cache if it is present.
     * <p>
     * More formally, if this cache contains a mapping from key <tt>k</tt> to
     * value <tt>v</tt> such that
     * <code>(key==null ?  k==null : key.equals(k))</code>, that mapping is removed.
     * (The cache can contain at most one such mapping.)
     *
     * <p>Returns <tt>true</tt> if this cache previously associated the key,
     * or <tt>false</tt> if the cache contained no mapping for the key.
     * <p>
     * The cache will not contain a mapping for the specified key once the
     * call returns.
     * <p>
     * If the cache is configured write-through the associated
     * {@link CacheWriter#delete(Object)} method will be called.
     * </p>
     * @param key key whose mapping is to be removed from the cache
     * @return returns false if there was no matching key
     * @throws NullPointerException  if key is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the remove
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#delete
     */
    boolean remove(K key);

    /**
     * Atomically removes the mapping for a key only if currently mapped to the
     * given value.
     * <p>
     * This is equivalent to:
     * <pre><code>
     * if (cache.containsKey(key) && equals(cache.get(key), oldValue) {
     *   cache.remove(key);
     *   return true;
     * } else {
     *   return false;
     * }
     * </code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through, and this method returns true,
     * the associated {@link CacheWriter#delete(Object)} method will be called.
     * </p>
     * @param key      key whose mapping is to be removed from the cache
     * @param oldValue value expected to be associated with the specified key
     * @return returns false if there was no matching key
     * @throws NullPointerException  if key is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem doing the remove
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#delete
     */
    boolean remove(K key, V oldValue);

    /**
     * Atomically removes the entry for a key only if currently mapped to some
     * value.
     * <p>
     * This is equivalent to:
     * <pre><code>
     * if (cache.containsKey(key)) {
     *   V oldValue = cache.get(key);
     *   cache.remove(key);
     *   return oldValue;
     * } else {
     *   return null;
     * }
     * </code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through the associated
     * {@link CacheWriter#delete(Object)} method will be called.
     * </p>
     *
     * @param key key with which the specified value is associated
     * @return the value if one existed or null if no mapping existed for this key
     * @throws NullPointerException  if the specified key or value is null.
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the remove
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#delete
     */
    V getAndRemove(K key);

    /**
     * Atomically replaces the entry for a key only if currently mapped to a
     * given value.
     * <p>
     * This is equivalent to:
     * <pre><code>
     * if (cache.containsKey(key) && equals(cache.get(key), oldValue)) {
     *  cache.put(key, newValue);
     * return true;
     * } else {
     *  return false;
     * }
     * </code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through, and this method returns true,
     * the associated {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     * @param key      key with which the specified value is associated
     * @param oldValue value expected to be associated with the specified key
     * @param newValue value to be associated with the specified key
     * @return <tt>true</tt> if the value was replaced
     * @throws NullPointerException  if key is null or if the values are null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the replace
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#write
     */
    boolean replace(K key, V oldValue, V newValue);

    /**
     * Atomically replaces the entry for a key only if currently mapped to some
     * value.
     * <p>
     * This is equivalent to
     * <pre><code>
     * if (cache.containsKey(key)) {
     *   cache.put(key, value);
     *   return true;
     * } else {
     *   return false;
     * }</code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through, and this method returns true,
     * the associated {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     * @param key  the key with which the specified value is associated
     * @param value the value to be associated with the specified key
     * @return <tt>true</tt> if the value was replaced
     * @throws NullPointerException  if key is null or if value is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the replace
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see #getAndReplace(Object, Object)
     * @see CacheWriter#write
     */
    boolean replace(K key, V value);

    /**
     * Atomically replaces the value for a given key if and only if there is a
     * value currently mapped by the key.
     * <p>
     * This is equivalent to
     * <pre><code>
     * if (cache.containsKey(key)) {
     *   V oldValue = cache.get(key);
     *   cache.put(key, value);
     *   return oldValue;
     * } else {
     *   return null;
     * }
     * </code></pre>
     * except that the action is performed atomically.
     * <p>
     * If the cache is configured write-through, and this method returns true,
     * the associated {@link CacheWriter#write(Cache.Entry)} method will be called.
     * </p>
     * @param key   key with which the specified value is associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with the specified key, or
     *         <tt>null</tt> if there was no mapping for the key.
     * @throws NullPointerException  if key is null or if value is null
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the replace
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see java.util.concurrent.ConcurrentMap#replace(Object, Object)
     * @see CacheWriter#write
     */
    V getAndReplace(K key, V value);

    /**
     * Removes entries for the specified keys.
     * <p>
     * The order in which the individual entries are removed is undefined.
     * <p>
     * For every entry in the key set, the following are called:
     * <ul>
     *   <li>any registered {@link CacheEntryRemovedListener}s</li>
     *   <li>if the cache is a write-through cache, the {@link CacheWriter}</li>
     * </ul>
     *
     * @param keys the keys to remove
     * @throws NullPointerException  if keys is null or if it contains a null key
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the remove
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see CacheWriter#deleteAll
     */
    void removeAll(Set<? extends K> keys);

    /**
     * Removes all of the mappings from this cache.
     * <p>
     * The order that the individual entries are removed is undefined.
     * <p>
     * For every mapping that exists the following are called:
     * <ul>
     *   <li>any registered {@link CacheEntryRemovedListener}s</li>
     *   <li>if the cache is a write-through cache, the {@link CacheWriter}</li>
     * </ul>
     * If the cache is empty, the {@link CacheWriter} is not called.
     * <p>
     * This is potentially an expensive operation as listeners are invoked.
     * Use {@link #clear()} to avoid this.
     *
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the remove
     * @see #clear()
     * @see CacheWriter#deleteAll
     */
    void removeAll();

    /**
     * Clears the contents of the cache, without notifying listeners or
     * {@link CacheWriter}s.
     *
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     * @throws CacheException        if there is a problem during the clear
     */
    void clear();

    /**
     * Provides a standard way to access the configuration of a cache using
     * JCache configuration or additional proprietary configuration.
     * <p>
     * The returned value must be immutable.
     * <p>
     * If the provider's implementation does not support the specified class,
     * the {@link IllegalArgumentException} is thrown.
     *
     * @param <C> the type of the Configuration
     * @param clazz the configuration interface or class to return. This includes
     *              {@link Configuration}.class and
     *              {@link javax.cache.configuration.CompleteConfiguration}s.
     * @return the requested implementation of {@link Configuration}
     * @throws IllegalArgumentException if the caching provider doesn't support
     *                                  the specified class.
     */
    <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz);

    /**
     * Invokes an {@link EntryProcessor} against the {@link Entry} specified by
     * the provided key.
     *
     * @param <T>            the type of the return value
     * @param key            the key to the entry
     * @param entryProcessor the {@link EntryProcessor} to invoke
     * @param arguments      additional arguments to pass to the
     *                       {@link EntryProcessor}
     * @return the result of the processing, if any, defined by the
     *         {@link EntryProcessor} implementation
     * @throws NullPointerException    if key or {@link EntryProcessor} is null
     * @throws IllegalStateException   if the cache is {@link #isClosed()}
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @throws EntryProcessorException if an exception is thrown by the {@link
     *                                 EntryProcessor}, a Caching Implementation
     *                                 must wrap any {@link Exception} thrown
     *                                 wrapped in an {@link EntryProcessorException}.
     * @see EntryProcessor
     */
    <T> T invoke(K key,
                 EntryProcessor<K, V, T> entryProcessor,
                 Object... arguments) throws EntryProcessorException;

    /**
     * Invokes an {@link EntryProcessor} against the set of {@link Entry}s
     * specified by the set of keys.
     * <p>
     * The order that the entries for the keys are processed is undefined.
     * Implementations may choose to process the entries in any order, including
     * concurrently.  Furthermore there is no guarantee implementations will
     * use the same {@link EntryProcessor} instance to process each entry, as
     * the case may be in a non-local cache topology.
     * <p>
     * The result of executing the {@link EntryProcessor} is returned as a
     * {@link Map} of {@link EntryProcessorResult}s, one result per key.  Should the
     * {@link EntryProcessor} or Caching implementation throw an exception, the
     * exception is wrapped and re-thrown when a call to
     * {@link javax.cache.processor.EntryProcessorResult#get()} is made.
     *
     * @param <T>            the type of the return value
     * @param keys           the set of keys for entries to process
     * @param entryProcessor the {@link EntryProcessor} to invoke
     * @param arguments      additional arguments to pass to the
     *                       {@link EntryProcessor}
     * @return the map of {@link EntryProcessorResult}s of the processing per key,
     * if any, defined by the {@link EntryProcessor} implementation.  No mappings
     * will be returned for {@link EntryProcessor}s that return a
     * <code>null</code> value for a key.
     * @throws NullPointerException    if keys or {@link EntryProcessor} are null
     * @throws IllegalStateException   if the cache is {@link #isClosed()}
     * @throws ClassCastException    if the implementation is configured to perform
     *                               runtime-type-checking, and the key or value
     *                               types are incompatible with those that have been
     *                               configured for the {@link Cache}
     * @see EntryProcessor
     */
    <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,
                                                  EntryProcessor<K, V, T>
                                                          entryProcessor,
                                                  Object... arguments);

    /**
     * Return the name of the cache.
     *
     * @return the name of the cache.
     */
    String getName();

    /**
     * Gets the {@link CacheManager} that owns and manages the {@link Cache}.
     *
     * @return the manager or <code>null</code> if the {@link Cache} is not
     *         managed
     */
    CacheManager getCacheManager();

    /**
     * Closing a {@link Cache} signals to the {@link CacheManager} that produced or
     * owns the {@link Cache} that it should no longer be managed. At this
     * point in time the {@link CacheManager}:
     * <ul>
     * <li>must close and release all resources being coordinated on behalf of the
     * Cache by the {@link CacheManager}. This includes calling the <code>close
     * </code> method on configured {@link CacheLoader},
     * {@link CacheWriter}, registered {@link CacheEntryListener}s and
     * {@link ExpiryPolicy} instances that implement the java.io.Closeable
     * interface.
     * <li>prevent events being delivered to configured {@link CacheEntryListener}s
     * registered on the {@link Cache}
     * </li>
     * <li>not return the name of the Cache when the CacheManager getCacheNames()
     * method is called</li>
     * </ul>
     * Once closed any attempt to use an operational method on a Cache will throw an
     * {@link IllegalStateException}.
     * <p>
     * Closing a Cache does not necessarily destroy the contents of a Cache.
     * It simply signals to the owning CacheManager that the Cache is no longer
     * required by the application and that future uses of a specific Cache instance
     * should not be permitted.
     * <p>
     * Depending on the implementation and Cache topology,
     * (e.g. a storage-backed or distributed cache), the contents of a closed Cache
     * may still be available and accessible by other applications, or, in fact, via
     * the Cache Manager that previously owned the Cache, if an application calls
     * getCache at some point in the future.
     *
     * @throws SecurityException when the operation could not be performed
     *                           due to the current security settings
     */
    void close();

    /**
     * Determines whether this Cache instance has been closed. A Cache is
     * considered closed if;
     * <ol>
     * <li>the {@link #close()} method has been called</li>
     * <li>the associated {@link #getCacheManager()} has been closed, or</li>
     * <li>the Cache has been removed from the associated
     * {@link #getCacheManager()}</li>
     * </ol>
     * <p>
     * This method generally cannot be called to determine whether a Cache instance
     * is valid or invalid. A typical client can determine that a Cache is invalid
     * by catching any exceptions that might be thrown when an operation is
     * attempted.
     *
     * @return true if this Cache instance is closed; false if it is still open
     */
    boolean isClosed();

    /**
     * Provides a standard way to access the underlying concrete caching
     * implementation to provide access to further, proprietary features.
     * <p>
     * If the provider's implementation does not support the specified class,
     * the {@link IllegalArgumentException} is thrown.
     *
     * @param <T> the type of the underlying {@link Cache} implementation
     * @param clazz the proprietary class or interface of the underlying concrete
     *              cache. It is this type that is returned.
     * @return an instance of the underlying concrete cache
     * @throws IllegalArgumentException if the caching provider doesn't support
     *                                  the specified class.
     * @throws SecurityException        when the operation could not be performed
     *                                  due to the current security settings
     */
    <T> T unwrap(java.lang.Class<T> clazz);

    /**
     * Registers a {@link CacheEntryListener}. The supplied
     * {@link CacheEntryListenerConfiguration} is used to instantiate a listener
     * and apply it to those events specified in the configuration.
     *
     * @param cacheEntryListenerConfiguration
     *         a factory and related configuration
     *         for creating the listener
     * @throws IllegalArgumentException is the same CacheEntryListenerConfiguration
     *                                  is used more than once
     * @throws IllegalStateException    if the cache is {@link #isClosed()}
     * @see CacheEntryListener
     */
    void registerCacheEntryListener(
            CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration);

    /**
     * Deregisters a listener, using the
     * {@link CacheEntryListenerConfiguration} that was used to register it.
     * <p>
     * Both listeners registered at configuration time,
     * and those created at runtime with {@link #registerCacheEntryListener} can
     * be deregistered.
     *
     * @param cacheEntryListenerConfiguration
     *         the factory and related configuration
     *         that was used to create the
     *         listener
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     */
    void deregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V>
                                              cacheEntryListenerConfiguration);

    /**
     * {@inheritDoc}
     * <p>
     * The ordering of iteration over entries is undefined.
     * <p>
     * During iteration, any entries that are removed will have their appropriate
     * CacheEntryRemovedListeners notified.
     * <p>
     * When iterating over a cache it must be assumed that the underlying
     * cache may be changing, with entries being added, removed, evicted
     * and expiring. {@link java.util.Iterator#next()} may therefore return
     * null.
     *
     * @throws IllegalStateException if the cache is {@link #isClosed()}
     */
    Iterator<Cache.Entry<K, V>> iterator();

    /**
     * A cache entry (key-value pair).
     */
    interface Entry<K, V> {

        /**
         * Returns the key corresponding to this entry.
         *
         * @return the key corresponding to this entry
         */
        K getKey();

        /**
         * Returns the value stored in the cache when this entry was created.
         *
         * @return the value corresponding to this entry
         */
        V getValue();

        /**
         * Provides a standard way to access the underlying concrete cache entry
         * implementation in order to provide access to further, proprietary features.
         * <p>
         * If the provider's implementation does not support the specified class,
         * the {@link IllegalArgumentException} is thrown.
         *
         * @param <T> the type of the underlying {@link Entry} implementation
         * @param clazz the proprietary class or interface of the underlying
         *              concrete cache. It is this type that is returned.
         * @return an instance of the underlying concrete cache
         * @throws IllegalArgumentException if the caching provider doesn't support
         *                                  the specified class.
         */
        <T> T unwrap(Class<T> clazz);
    }
}

缓存类型安全

Java Caching API 大量使用了 JSR-14 中定义的泛型,以便在采用缓存时能够开发编译时类型安全的应用程序。

编译时的类型安全性不能保证运行时类型正确性。 对于某些缓存实现,特别是那些跨Java进程存储或访问条目的缓存实现,Java运行时类型信息擦除以及无法获取和传输泛型信息可能意味着应用程序类型无法确保此类中缓存操作的类型安全性。 始终应注意确保使用适当的键和值类型配置缓存,以便实现可以根据需要或要求执行类型检查。

编译时类型安全

编译时类型安全由泛型来保证。

例1

下面的这个例子中,Key 类型是 String ,Value 类型是 Integer 。当与 Cache 交互时使用了不兼容的类型时会报编译时错误。

Configuration config = new MutableConfiguration();

//create the cache
cacheManager.createCache(cacheName, config);

//... then later to get the cache
Cache<String, Integer> cache = cacheManager.getCache(cacheName);

//use the cache
String key = "key";
Integer value1 = 1;
cache.put("key", value1);
Integer value2 = cache.get(key);


//the following will not compile - incorrect types specified
//cache.put(2, "some value");

尽管可以通过使用原始类型(不指定泛型类型参数)声明一个Cache来规避编译时类型安全检查,但不建议这样做,因为它允许发生简单的编程错误。

例2

下面这个例子中, Cache 被声明为一个原始类型,这里面不会做编译时类型检查(尽管会生成类型警告)

Configuration config = new MutableConfiguration();
cacheManager.createCache(cacheName, config);

//... then later to get the cache without type information
Cache cache = cacheManager.getCache(cacheName);
String key = "key";
Integer value1 = 1;
cache.put("key", value1);

cache.put(value1, "key1");  //not intended but will still compile and execute!
Integer value2 = (Integer) cache.get(key);
assertEquals(value1, value2);

运行时类型安全

除了编译时类型安全,开发者可以通过配置 Cache 的 key 和 Value 的类型来保证运行时类型安全。例如, MutableConfiguration 类提供了下面的方法来定义需要的key 和 Value 类型。

/**
 * Sets the expected type of keys and values for a {@link Cache}
 * configured with this {@link Configuration}. Setting both to
 * <code>Object.class</code> means type-safety checks are not required.
 * <p/>
 * This is used by {@link CacheManager} to ensure that the key and value
 * types are the same as those configured for the {@link Cache} prior to
 * returning a requested cache from this method.
 * <p/>
 * Implementations may further perform type checking on mutative cache operations
 * and throw a {@link ClassCastException} if these checks fail.
 *
 * @param keyType   the expected key type
 * @param valueType the expected value type
 * @return the {@link MutableConfiguration} to permit fluent-style method calls
 * @throws NullPointerException should the key or value type be null
 * @see CacheManager#getCache(String, Class, Class)
 */
public MutableConfiguration<K, V> setTypes(Class<K> keyType, Class<V> valueType)

当缓存配置定义了 key 和 Value 的类型,由 CacheManager.getCache 返回的 Cache 必须强制入参的的键和值类型与配置的键和值类型相同。此时必须使用下面的方法来获取缓存

<K, V> Cache<K, V> getCache(String cacheName, Class<K> keyType, Class<V> valueType);

当使用上面的方法时,缓存实现必须确保返回的缓存显式配置了键值类型。这给开发者提供了相比仅使用泛型更高级别的安全性。

实现可以在可变缓存操作时做运行时类键值检查。

当配置未定义所需的键和值类型,或者它们都定义为 Object.class 时,在请求缓存时,不需要实现来执行运行时类型检查。 要在不检查使用的情况下请求缓存使用:

<K, V> Cache<K, V> Cache getCache(String cacheName);

在没有配置键值类型的情况下,尝试使用具有特定类型参数的 getCache 会引发 IllegalArgumentException 。 对任何具有特定键和值类型配置的键或没有定义键或值类型或两者均为 Object.class 的任何 Cache 使用不带特定类型参数的 getCache 时,将返回不进行任何类型检查的 Cache

例子

在此示例中,缓存配置为具有字符串键类型和整数值类型。 然后,实现将确保声明的类型与配置的缓存匹配,否则将抛出 IllegalArgumentException

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

MutableConfiguration<String, Integer> config = new
    MutableConfiguration<String, Integer>();
config.setTypes(String.class, Integer.class);
cacheManager.createCache("simpleCache", config);

//... then later to get the cache without type information
Cache<String, Integer> simpleCache = cacheManager.getCache("simpleCache",
    String.class, Integer.class);

simpleCache.put("key1", 3);
Integer value2 = simpleCache.get("key1");

尽管 Java Caching API 提供了用于编译和运行时类型安全的机制,但是类型检查仅适用于键和值的可验证类型,包括所有通用集合类型。 例如,类型 List<MyClass> 在运行时不可修改,因此只能与类型 List.class 进行比较。

过期策略

如果条目已过期,它就不能从缓存中被获取到。如果 Cache 没有配置过期策略,默认用不过期

虽然“用不过期”策略不自动过期条目,但是缓存实现可以支持手动驱逐。

过期策略在配置时指定,过期策略需要实现 ExpiryPolicy 接口,接口定义如下:

package javax.cache.expiry;

/**
 * Defines functions to determine when cache entries will expire based on
 * creation, access and modification operations.
 * <p>
 * Each of the functions return a new {@link Duration} that specifies the
 * amount of time that must pass before a cache entry is considered expired.
 * {@link Duration} has constants defined for useful durations.
 *
 * @author Brian Oliver
 * @author Greg Luck
 * @since 1.0
 * @see Duration
 */
public interface ExpiryPolicy {

  /**
   * Gets the {@link Duration} before a newly created Cache.Entry is considered
   * expired.
   * <p>
   * This method is called by a caching implementation after a Cache.Entry is
   * created, but before a Cache.Entry is added to a cache, to determine the
   * {@link Duration} before an entry expires.  If a {@link Duration#ZERO}
   * is returned the new Cache.Entry is considered to be already expired and
   * will not be added to the Cache.
   * <p>
   * Should an exception occur while determining the Duration, an implementation
   * specific default {@link Duration} will be used.
   *
   * @return the new {@link Duration} before a created entry expires
   */
  Duration getExpiryForCreation();

  /**
   * Gets the {@link Duration} before an accessed Cache.Entry is
   * considered expired.
   * <p>
   * This method is called by a caching implementation after a Cache.Entry is
   * accessed to determine the {@link Duration} before an entry expires.  If a
   * {@link Duration#ZERO} is returned a Cache.Entry will be
   * considered immediately expired.  Returning <code>null</code> will result
   * in no change to the previously understood expiry {@link Duration}.
   * <p>
   * Should an exception occur while determining the Duration, an implementation
   * specific default Duration will be used.
   *
   * @return the new {@link Duration} before an accessed entry expires
   */
  Duration  getExpiryForAccess();

  /**
   * Gets the {@link Duration} before an updated Cache.Entry is considered
   * expired.
   * <p>
   * This method is called by the caching implementation after a Cache.Entry is
   * updated to determine the {@link Duration} before the updated entry expires.
   * If a {@link Duration#ZERO} is returned a Cache.Entry is considered
   * immediately expired.  Returning <code>null</code> will result in no change
   * to the previously understood expiry {@link Duration}.
   * <p>
   * Should an exception occur while determining the Duration, an implementation
   * specific default Duration will be used.
   *
   * @return the new {@link Duration} before an updated entry expires
   */
  Duration getExpiryForUpdate();
}

缓存条目在执行完指定操作一段时间后过期,这个时间间隔由 javax.cache.expiry.Duration 来定义。 Durationjava.util.concurrent.TimeUnitlong durationAmount 两部分组成,最小的时间单位是 TimeUnit.MILLISECONDS ;

过期时间由配置的过期策略和执行的缓存操作有关。下面是 ExpiryPolicy 接口中对于指定操作获取对应的时间间隔的方法

getExpiryForCreation()
getExpiryForAccess()
getExpiryForUpdate()

当一个缓存实现调用了上面的三个方法,会返回下面几种结果其中之一:

  • 一个新的有效时长
  • Duration.ZERO ,代表条目立即过期
  • nullgetExpiryForUpdate()getExpiryForAccess() 可能返回,代表不修改条目当前的有效时长。

除了 Duration#ZERO ,还定义了如下常量

/**
   * ETERNAL (forever).
   */
  public static final Duration ETERNAL = new Duration();

  /**
   * One day.
   */
  public static final Duration ONE_DAY = new Duration(DAYS, 1);

  /**
   * One hour.
   */
  public static final Duration ONE_HOUR = new Duration(HOURS, 1);

  /**
   * Thirty minutes.
   */
  public static final Duration THIRTY_MINUTES = new Duration(MINUTES, 30);

  /**
   * Twenty minutes.
   */
  public static final Duration TWENTY_MINUTES = new Duration(MINUTES, 20);

  /**
   * Ten minutes.
   */
  public static final Duration TEN_MINUTES = new Duration(MINUTES, 10);

  /**
   * Five minutes.
   */
  public static final Duration FIVE_MINUTES = new Duration(MINUTES, 5);

  /**
   * One minute.
   */
  public static final Duration ONE_MINUTE = new Duration(MINUTES, 1);

  /**
   * Zero (no time).
   */
  public static final Duration ZERO = new Duration(SECONDS, 0);

下面的表格中描述了缓存方法如何与过期策略进行交互

缓存方法 ExpiryPolicy.getExpiryForCreation
是否调用
ExpiryPolicy.getExpiryForAccess
是否调用
ExpiryPolicy.getExpiryForUpdate
是否调用
boolean containsKey(K key) NO NO NO
V get(K key) NO (unless read-though caused a load) YES NO
Map<K,V> getAll(Collection<? extends K> keys) No (unless read-though caused a load) YES NO
V getAndPut(K key, V value) YES (when the key is not associated with an existing value) NO YES (when the key is associated with an existing value)
V getAndRemove(K key) NO NO NO
V getAndReplace(K key, V value) NO NO Yes (when the key is associated with an existing value)
CacheManager getCacheManager() NO NO NO
CacheConfiguration getConfiguration() NO NO NO
String getName() NO NO NO
Iterator<Cache.Entry<K, V>> iterator() NO YES (when an entry is visited by an iterator) NO
void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) YES (when a key is not associated with a loaded value) NO YES (when a key is associated with a loaded value and the value should be replaced)
void put(K key, V value) YES (when the key is not associated with an existing value) NO YES (when the key is associated with an existing value)
void putAll(Map<? extends K,? extends V> map) YES (when the key is not associated with an existing value) NO YES(when the key is associated with an existing value)
boolean putIfAbsent(K key, V value) YES (when the key is not associated with an existing value) NO NO
boolean remove(K key) NO NO NO
boolean remove(K key, V oldValue) NO YES (when the old value does not match the existing value) NO
void removeAll() NO NO NO
void removeAll(Set<? extends K> keys) NO NO NO
<T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)entryProcessor); Yes (for the following cases:
(1) setValue called and entry did not exist for key before invoke was called.
(2) if read-through enabled and getValue() is called and causes a new entry to be loaded for key)
Yes (when getValue was called and no other mutations occurred during entry processor execution. note: Create, modify or remove take precedence over Access) Yes (when setValue was called and the entry already existed before entry processor was called)
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments); 同 invoke 同 invoke 同 invoke
boolean replace(K key, V value) NO NO YES (when the key is associated with an existing value)
boolean replace(K key, V oldValue, V newValue) NO YES (when value is not replaced) YES (when value is replaced)
<T> T unwrap(Class<T> cls) NO NO NO

本规范预定义了五种过期策略,在 javax.cache.expiry 包下可以找到对应实现:

CreatedExpiryPolicy
ModifiedExpiryPolicy
AccessedExpiryPolicy
TouchedExpiryPolicy
EternalExpiryPolicy

集成

javax.cache.integration 包里提供了一些便于与外部资源整合的工具。

下面定义了 CacheLoaderCacheWriter 类:

package javax.cache.integration;

import javax.cache.configuration.CompleteConfiguration;
import java.util.Map;

/**
 * Used when a cache is read-through or when loading data into a cache via the
 * {@link javax.cache.Cache#loadAll(java.util.Set, boolean,
 * CompletionListener)} method.
 *
 * @param <K> the type of keys handled by this loader
 * @param <V> the type of values generated by this loader
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @see CompleteConfiguration#isReadThrough()
 * @see CacheWriter
 * @since 1.0
 */
public interface CacheLoader<K, V> {

  /**
   * Loads an object. Application developers should implement this
   * method to customize the loading of a value for a cache entry. This method
   * is called by a cache when a requested entry is not in the cache. If
   * the object can't be loaded <code>null</code> should be returned.
   *
   * @param key the key identifying the object being loaded
   * @return The value for the entry that is to be stored in the cache or
   *         <code>null</code> if the object can't be loaded
   * @throws CacheLoaderException if there is problem executing the loader.
   */
  V load(K key) throws CacheLoaderException;



  /**
   * Loads multiple objects. Application developers should implement this
   * method to customize the loading of cache entries. This method is called
   * when the requested object is not in the cache. If an object can't be loaded,
   * it is not returned in the resulting map.
   *
   * @param keys keys identifying the values to be loaded
   * @return A map of key, values to be stored in the cache.
   * @throws CacheLoaderException if there is problem executing the loader.
   */
  Map<K, V> loadAll(Iterable<? extends K> keys) throws CacheLoaderException;
}
package javax.cache.integration;


import javax.cache.Cache;
import java.util.Collection;

/**
 * A CacheWriter is used for write-through to an external resource.
 * <p>
 * Under Default Consistency, the non-batch writer methods are atomic with respect
 * to the corresponding cache operation.
 * <p>
 * For batch methods under Default Consistency, the entire cache operation
 * is not required to be atomic in {@link Cache} and is therefore not required to
 * be atomic in the writer. As individual writer operations can fail, cache
 * operations are not required to occur until after the writer batch method has
 * returned or, in the case of partial success, thrown an exception. In the case
 * of partial success, the collection of entries return must only contain
 * those entries that failed.
 * <p>
 * The entry passed into {@link #write(Cache.Entry)} is independent
 * of the cache mapping for that key, meaning that if the value changes in the
 * cache or is removed it does not change the entry.
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 * @author Greg Luck
 * @author Brian Oliver
 * @see CacheLoader
 * @since 1.0
 */
public interface CacheWriter<K, V> {


  /**
   * Write the specified value under the specified key to the external resource.
   * <p>
   * This method is intended to support both key/value creation and value update
   * for a specific key.
   *
   * @param entry the entry to be written
   * @throws CacheWriterException if the write fails. If thrown the
   *                              cache mutation will not occur.
   */
  void write(Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException;

  /**
   * Write the specified entries to the external resource. This method is intended
   * to support both insert and update.
   * <p>
   * The order that individual writes occur is undefined, as
   * {@link Cache#putAll(java.util.Map)} also has undefined ordering.
   * <p>
   * If this operation fails (by throwing an exception) after a partial success,
   * the writer must remove any successfully written entries from the entries
   * collection so that the caching implementation knows what succeeded and can
   * mutate the cache.
   *
   * @param entries a mutable collection to write. Upon invocation, it contains
   *                the entries to write for write-through. Upon return the
   *                collection must only contain entries that were not
   *                successfully written. (see partial success above)
   * @throws CacheWriterException if one or more of the writes fail. If
   *                              thrown cache mutations will occur for
   *                              entries that succeeded.
   */
  void writeAll(Collection<Cache.Entry<? extends K, ? extends V>> entries) throws
      CacheWriterException;


  /**
   * Delete the cache entry from the external resource.
   * <p>
   * Expiry of a cache entry is not a delete hence will not cause this method to
   * be invoked.
   * <p>
   * This method is invoked even if no mapping for the key exists.
   *
   * @param key the key that is used for the delete operation
   * @throws CacheWriterException if delete fails. If thrown the cache delete will
   *                              not occur.
   */
  void delete(Object key) throws CacheWriterException;


  /**
   * Remove data and keys from the external resource for the given collection of
   * keys, if present.
   * <p>
   * The order that individual deletes occur is undefined, as
   * {@link Cache#removeAll(java.util.Set)} also has undefined ordering.
   * <p>
   * If this operation fails (by throwing an exception) after a partial success,
   * the writer must remove any successfully written entries from the entries
   * collection so that the caching implementation knows what succeeded and can
   * mutate the cache.
   * <p>
   * Expiry of a cache entry is not a delete hence will not cause this method to
   * be invoked.
   * <p>
   * This method may include keys even if there is no mapping for that key,
   * in which case the data represented by that key should be removed from the
   * underlying resource.
   *
   * @param keys a mutable collection of keys for entries to delete. Upon
   *             invocation, it contains the keys to delete for write-through.
   *             Upon return the collection must only contain the keys that were
   *             not successfully deleted. (see partial success above)
   * @throws CacheWriterException if one or more deletes fail. If thrown
   *                              cache deletes will occur for entries that
   *                              succeeded.
   */
  void deleteAll(Collection<?> keys) throws CacheWriterException;
}

缓存加载

loadAll 方法用来将外部资源加载到缓存中,定义如下:

/**
* Asynchronously loads the specified entries into the cache using the
* configured {@link CacheLoader} for the given keys.
* <p>
* If an entry for a key already exists in the Cache, a value will be loaded
* if and only if <code>replaceExistingValues</code> is true.   If no loader
* is configured for the cache, no objects will be loaded.  If a problem is
* encountered during the retrieving or loading of the objects,
* an exception is provided to the {@link CompletionListener}.  Once the
* operation has completed, the specified CompletionListener is notified.
* <p>
* Implementations may choose to load multiple keys from the provided
* {@link Set} in parallel.  Iteration however must not occur in parallel,
* thus allow for non-thread-safe {@link Set}s to be used.
* <p>
* The thread on which the completion listener is called is implementation
* dependent. An implementation may also choose to serialize calls to
* different CompletionListeners rather than use a thread per
* CompletionListener.
*
* @param keys                  the keys to load
* @param replaceExistingValues when true existing values in the Cache will
*                              be replaced by those loaded from a CacheLoader
* @param completionListener    the CompletionListener (may be null)
* @throws NullPointerException  if keys is null or if keys contains a null.
* @throws IllegalStateException if the cache is {@link #isClosed()}
* @throws CacheException        thrown if there is a problem performing the
*                               load. This may also be thrown on calling if
*                               there are insufficient threads available to
*                               perform the load.
* @throws ClassCastException    if the implementation is configured to perform
*                               runtime-type-checking, and the key or value
*                               types are incompatible with those that have been
*                               configured for the {@link Cache}
*/
void loadAll(Set<? extends K> keys, boolean replaceExistingValues,
CompletionListener completionListener);

为了使用这个方法,在创建缓存时必须配置 CacheLoader 。不需要将缓存配置为 read-through 模式。

加载可能耗时较长,因为这个原因,可以给该方法传递一个 CompletionListener ,来接收加载完成或加载异常的通知。该接口的定义如下:

package javax.cache.integration;

/**
 * A CompletionListener is implemented by an application when it needs to be
 * notified of the completion of some Cache operation.
 * <p>
 * When the operation is complete, the Cache provider notifies the application
 * by calling the {@link #onCompletion()} method of the {@link
 * CompletionListener}.
 * <p>
 * If the operation fails for any reason, the Cache provider calls the
 * {@link #onException(Exception)} method of the {@link CompletionListener}.
 * <p>
 * To support a Java Future-based approach to synchronously wait for a Cache
 * operation to complete, use a {@link CompletionListenerFuture}.
 * <p>
 * A Cache provider will use an implementation specific thread to call methods
 * on this interface.
 *
 * @author Brian Oliver
 * @since 1.0
 * @see CompletionListenerFuture
 */
public interface CompletionListener {

  /**
   * Notifies the application that the operation completed successfully.
   */
  void onCompletion();

  /**
   * Notifies the application that the operation failed.
   *
   * @param e the Exception that occurred
   */
  void onException(Exception e);
}

本规范也提供了一个阻塞版本的 CompletionListener 实现, CompletionListenerFuture 。它实现了 CompletionListenerFuture 两个接口。如果 onException(Exception e) 方法被调用,异常会被重新封装成 ExecutionExceptionFuture get()/get(long timeout, TimeUnit unit) 重抛。

例子

HashSet<String> keys = new HashSet<>();
keys.add("23432lkj");
keys.add("4fsdldkj");


//create a completion future to use to wait for loadAll
CompletionListenerFuture future = new CompletionListenerFuture();

//load the values for the set of keys, replacing those that may already
//exist in the cache
cache.loadAll(keys, true, future);

//wait for the cache to load the keys
try {
  future.get();
} catch (InterruptedException e) {
  //future interrupted
  e.printStackTrace();
} catch (ExecutionException e) {
  //throwable was what was sent to onException(Exception e)
  Throwable throwable = e.getCause();
}

loadAll 方法用来从外部资源预加载数据非常有用。一种用法是应用程序假定缓存一定存在,不然会有异常;另一种是用来做缓存预热,如果缓存中没有数据,不会导致错误,但是会对性能有影响。

Read-Through 模式

Read-Through 模式和非 Read-Through 模式几乎完全相同,不同之处就在于当缓存中没有数据时,会调用 CacheLoader 去加载。

通过 MutableConfiguration 上调用 setReadThrough(boolean isReadThrough) 来配置该模式,与该模式配套的 CacheLoader Factory 也必须配置。

下表用于描述 Cache 的那些方法会使用该模式加载数据:

方法 启用 Read-Through
boolean containsKey(K key) NO
V get(K key) YES
Map<K,V> getAll(Collection<? extends K> keys) YES, 调用 loadAll()
V getAndPut(K key, V value) NO
V getAndRemove(K key) NO
V getAndReplace(K key, V value) NO
<T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)entryProcessor); getValue() 被调用时
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,EntryProcessor<K, V, T> entryProcessor, Object... arguments); getValue() 被调用时
Iterator<Cache.Entry<K, V>> iterator() NO
void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) YES,使用 CacheLoader.loadAll() ,不管当前缓存是否是 Read-Through 模式
void put(K key, V value) NO
void putAll(Map<? extends K,? extends V> map) NO
boolean putIfAbsent(K key, V value) NO
boolean remove(K key) NO
boolean remove(K key, V oldValue) NO
void removeAll() NO
void removeAll(Set<? extends K> keys) NO
boolean replace(K key, V value) NO
boolean replace(K key, V oldValue, V newValue) NO

Read-Through 模式的使用惯例是懒加载。在屏蔽缓存加载的细节上,这种模式也很有用。

当 一些或全部数据需要被预先加载时,使用 loadAll 方法。

Write-Through 模式

Write-Through 模式和非 Write-Through 模式几乎完全相同,不同之处就在于当缓存数据变更时,会调用 CacheWriter

通过 MutableConfiguration 上调用 setWriteThrough(boolean isWriteThrough) 来配置该模式,与该模式配套的 CacheWriter Factory 也必须配置。 CacheWriter 用来更新/删除外部资源。

下表用于描述 Cache 的那些方法会使用该模式将变更写到外部资源:

方法 启用 Write-Through
boolean containsKey(K key) NO
V get(K key) NO
Map<K,V> getAll(Collection<? extends K> keys) NO
V getAndPut(K key, V value) YES
V getAndRemove(K key) YES
V getAndReplace(K key, V value) 当Key能查到对应Value时
<T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)entryProcessor); setValue() 被调用时
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,EntryProcessor<K, V, T> entryProcessor, Object... arguments); setValue() 被调用时
Iterator<Cache.Entry<K, V>> iterator() NO
void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) NO
void put(K key, V value) YES
void putAll(Map<? extends K,? extends V> map) YES,将会调用 writeAll
boolean putIfAbsent(K key, V value) 返回true时
boolean remove(K key) YES,不管key是否存在
boolean remove(K key, V oldValue) 返回true时
void removeAll() YES,将会调用 deleteAll
void removeAll(Set<? extends K> keys) YES, 不管key是否存在
void clear() NO
boolean replace(K key, V value) 返回true时
boolean replace(K key, V oldValue, V newValue) 返回true时

Write-Through 主要用于在缓存更新时对外部资源进行更新。该模式对用户屏蔽了写入外部资源的细节。

缓存条目监听器

javax.cache.event 包下包括了用户处理 Cache 产生事件的类和接口。

事件和事件类型

CacheEntryEvent 定义如下:

package javax.cache.event;

import javax.cache.Cache;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import java.util.EventObject;

/**
 * A Cache entry event base class.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Greg Luck
 * @since 1.0
 */
public abstract class CacheEntryEvent<K, V> extends EventObject
    implements Cache.Entry<K, V> {

  private EventType eventType;

  /**
   * Constructs a cache entry event from a given cache as source
   *
   * @param source the cache that originated the event
   * @param eventType the event type for this event
   */
  public CacheEntryEvent(Cache source, EventType eventType) {
    super(source);
    this.eventType = eventType;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final Cache getSource() {
    return (Cache) super.getSource();
  }

  /**
   * Returns the value stored in the cache when this entry was created or updated.
   * <p>
   * The value will be available
   * for {@link CacheEntryCreatedListener} and {@link CacheEntryUpdatedListener}.
   * Returns the same value as {@link #getOldValue()} for
   * {@link CacheEntryExpiredListener} and {@link CacheEntryRemovedListener}.
   * Cache clients that need to maintain compatibility with JSR107 version 1.0
   * cache implementations, need to use this method for retrieving the expired
   * or removed value. When using cache implementations compatible with JSR107
   * version 1.1, clients should prefer the method {@link #getOldValue()}.
   *
   * @return the value corresponding to this entry
   * @see #getOldValue()
   */
  @Override
  public abstract V getValue();

  /**
   * Returns the previous value that existed for entry in the cache before
   * modification or removal.
   *
   * The old value will be available
   * for {@link CacheEntryUpdatedListener}, {@link CacheEntryExpiredListener}
   * and {@link CacheEntryRemovedListener}
   * if {@link CacheEntryListenerConfiguration#isOldValueRequired()} is true.
   * The old value may be available for {@link CacheEntryUpdatedListener},
   * {@link CacheEntryExpiredListener} and {@link CacheEntryRemovedListener}
   * if {@link CacheEntryListenerConfiguration#isOldValueRequired()} is false.
   *
   * @return the previous value or <code>null</code> if there was no previous
   * value or the previous value is not available
   */
  public abstract V getOldValue();

  /**
   * Whether the old value is available. The old value will be available
   * for {@link CacheEntryUpdatedListener}, {@link CacheEntryExpiredListener}
   * and {@link CacheEntryRemovedListener}
   * if {@link CacheEntryListenerConfiguration#isOldValueRequired()} is true.
   * The old value <b>may</b> be available for {@link CacheEntryUpdatedListener},
   * {@link CacheEntryExpiredListener} and {@link CacheEntryRemovedListener}
   * if {@link CacheEntryListenerConfiguration#isOldValueRequired()} is false.
   *
   * @return true if the old value is definitely available
   */
  public abstract boolean isOldValueAvailable();

  /**
   * Gets the event type of this event
   *
   * @return the event type.
   */
  public final EventType getEventType() {
    return eventType;
  }
}

EventType 定义如下:

package javax.cache.event;

/**
 * The type of event received by the listener.
 *
 * @author Greg Luck
 * @since 1.0
 */
public enum EventType {

  /**
   * An event type indicating that the cache entry was created.
   */
  CREATED,

  /**
   * An event type indicating that the cache entry was updated. i.e. a previous
   * mapping existed
   */
  UPDATED,


  /**
   * An event type indicating that the cache entry was removed.
   */
  REMOVED,


  /**
   * An event type indicating that the cache entry has expired.
   */
  EXPIRED

}

CacheEntryEventgetValuegetOldValue 的可用性

下表总结了 isOldValueRequired 为 true 时,各个监听器调用 getValuegetOldValue 时的返回值。

CacheEntryEvent CacheEntryCreatedListener CacheEntryUpdatedListener CacheEntryRemovedListener
CacheEntryExpiredListener
getValue 新值 新值 旧值
getOldValue null 旧值 旧值

下表总结了 isOldValueRequired 为 false 时,各个监听器调用 getValuegetOldValue 时的返回值。

CacheEntryEvent CacheEntryCreatedListener CacheEntryUpdatedListener CacheEntryRemovedListener
CacheEntryExpiredListener
getValue 新值 新值 旧值或 null
getOldValue null 旧值或 null 旧值 null

isOldValueRequired 为 false 时,缓存实现可以自由选择是否返回旧值。

缓存条目监听器

CacheEntryListener 通过 CacheEntryListenerConfiguration 注册到 Cache 中。 CacheEntryListener 接口定义如下:

package javax.cache.event;

import java.util.EventListener;

/**
 * A tagging interface for cache entry listeners.
 * <p>
 * Sub-interfaces exist for the various cache events allowing a listener to be
 * created that implements only those listeners it is interested in.
 * <p>
 * Listeners should be implemented with care. In particular it is important to
 * consider their impact on performance and latency.
 * <p>
 * Listeners:
 * <ul>
 * <li>are fired after the entry is mutated in the cache</li>
 * <li>if synchronous are fired, for a given key, in the order that events
 * occur</li>
 * <li>block the calling thread until the listener returns,
 * where the listener was registered as synchronous</li>
 * <li>that are asynchronous iterate through multiple events with an undefined
 * ordering, except that events on the same key are in the order that the
 * events occur.</li>
 * </ul>
 * Listeners follow the observer pattern. An exception thrown by a
 * listener does not cause the cache operation to fail.
 * <p>
 * Listeners can only throw {@link CacheEntryListenerException}. Caching
 * implementations must catch any other {@link Exception} from a listener, then
 * wrap and rethrow it as a {@link CacheEntryListenerException}.
 * <p>
 * A listener that mutates a cache on the CacheManager may cause a deadlock.
 * Detection and response to deadlocks is implementation specific.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Yannis Cosmadopoulos
 * @author Greg Luck
 * @see CacheEntryCreatedListener
 * @see CacheEntryUpdatedListener
 * @see CacheEntryRemovedListener
 * @see CacheEntryExpiredListener
 * @see EventType
 * @since 1.0
 */
public interface CacheEntryListener<K, V> extends EventListener {

}

对应不同的 EventType ,本规范定义了四个子接口。

/**
 * Invoked after a cache entry is created, or if a batch call is made, after the
 * entries are created.
 * <p>
 * If an entry for the key existed prior to the operation it is not invoked,
 * instead {@link CacheEntryUpdatedListener} is invoked.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Yannis Cosmadopoulos
 * @author Greg Luck
 * @see CacheEntryUpdatedListener
 * @since 1.0
 */
public interface CacheEntryCreatedListener<K, V> extends CacheEntryListener<K, V> {

  /**
   * Called after one or more entries have been created.
   *
   * @param events The entries just created.
   * @throws CacheEntryListenerException if there is problem executing the listener
   */
  void onCreated(Iterable<CacheEntryEvent<? extends K, ? extends V>> events)
      throws CacheEntryListenerException;

}
package javax.cache.event;

/**
 * Invoked if an existing cache entry is updated, or if a batch call is made,
 * after the entries are updated.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Yannis Cosmadopoulos
 * @author Greg Luck
 * @see CacheEntryCreatedListener
 * @since 1.0
 */
public interface CacheEntryUpdatedListener<K, V> extends CacheEntryListener<K, V> {

  /**
   * Called after one or more entries have been updated.
   *
   * @param events The entries just updated.
   * @throws CacheEntryListenerException if there is problem executing the listener
   */
  void onUpdated(Iterable<CacheEntryEvent<? extends K, ? extends V>> events)
      throws CacheEntryListenerException;
}
package javax.cache.event;


/**
 * Invoked if a cache entry or entries are evicted due to expiration.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Greg Luck
 * @since 1.0
 */
public interface CacheEntryExpiredListener<K, V> extends CacheEntryListener<K, V> {

  /**
   * Called after one or more entries have been expired by the cache. This is not
   * necessarily when an entry is expired, but when the cache detects the expiry.
   *
   * @param events The entries just removed.
   * @throws CacheEntryListenerException if there is problem executing the listener
   */
  void onExpired(Iterable<CacheEntryEvent<? extends K, ? extends V>> events)
      throws CacheEntryListenerException;

}
package javax.cache.event;

/**
 * Invoked if a cache entry is removed, or if a batch call is made, after the
 * entries are removed.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Yannis Cosmadopoulos
 * @author Greg Luck
 * @since 1.0
 */
public interface CacheEntryRemovedListener<K, V> extends CacheEntryListener<K, V> {

  /**
   * Called after one or more entries have been removed. If no entry existed for
   * a key an event is not raised for it.
   *
   * @param events The entries just removed.
   * @throws CacheEntryListenerException if there is problem executing the listener
   */
  void onRemoved(Iterable<CacheEntryEvent<? extends K, ? extends V>> events)
      throws CacheEntryListenerException;


}

这样设计的初衷是为了很好的支持分布式监听器的实现。

监听器注册

监听器和 Cache 可能不在同一个进程。为了避免监听器实例不支持序列化的情况,我们使用 CacheEntryListenerConfiguration 来传递配置。该配置可以在初始化时使用 MutableConfiguation.addCacheEntryListenerConfiguration 配置,也可以使用 Cache.registerCacheEntryListener 在运行时注册。

运行时注销监听器可以使用 Cache.deregisterCacheEntryListener

package javax.cache.configuration;

import javax.cache.event.CacheEntryEventFilter;
import javax.cache.event.CacheEntryListener;
import java.io.Serializable;

/**
 * Defines the configuration requirements for a
 * {@link CacheEntryListener} and a {@link Factory} for its
 * creation.
 *
 * @param <K> the type of keys
 * @param <V> the type of values
 * @author Brian Oliver
 * @author Greg Luck
 * @since 1.0
 */
public interface CacheEntryListenerConfiguration<K, V> extends Serializable {
  /**
   * Obtains the {@link Factory} for the
   * {@link CacheEntryListener}.
   *
   * @return the {@link Factory} for the
   *         {@link CacheEntryListener}
   */
  Factory<CacheEntryListener<? super K, ? super V>> getCacheEntryListenerFactory();

  /**
   * Determines if the old value should be provided to the
   * {@link CacheEntryListener}.
   *
   * @return <code>true</code> if the old value is required by the
   *         {@link CacheEntryListener}
   */
  boolean isOldValueRequired();

  /**
   * Obtains the {@link Factory} for the {@link CacheEntryEventFilter} that should be
   * applied prior to notifying the {@link CacheEntryListener}.
   * <p>
   * When <code>null</code> no filtering is applied and all appropriate events
   * are notified.
   *
   * @return the {@link Factory} for the
   *         {@link CacheEntryEventFilter} or <code>null</code>
   *         if no filtering is required
   */
  Factory<CacheEntryEventFilter<? super K, ? super V>>
  getCacheEntryEventFilterFactory();

  /**
   * Determines if the thread that caused an event to be created should be
   * blocked (not return from the operation causing the event) until the
   * {@link CacheEntryListener} has been notified.
   *
   * @return <code>true</code> if the thread that created the event should block
   */
  boolean isSynchronous();
}

为了便于使用,本规范提供了一个实现 javax.cache.configuration.MutableCacheEntryListenerConfiguration

监听器的调用

缓存监听器:

  • 条目变更时被触发。
  • 如果是同步的,对应一个指定的 key,事件处理的顺序是事件发生的顺序,在监听器处理完之前会阻塞调用线程。
  • 如果是异步的,多个事件的遍历顺序未定义,但是对于同一个key 的事件,必须按照事件发生的顺序来处理。

监听器使用观察者模式,缓存监听器内的异常不会导致缓存操作失败。

监听器可能会导致死锁,对死锁的检测和响应取决于实现。

对于每个事件来说,特定的监听器最多只被调用一次。该监听器的调用指的是集群范围内的调用,而不是每个节点的。

监听器和原始的事件可以不在同一个进程。在分布式实现中,监听器可以位于任何位置。

监听器可能有对应的事件过滤器 CacheEntryEventFilter ,这是 CacheEntryListenerConfiguration 配置的一部分。接口定义如下:

package javax.cache.event;

/**
 * A function that may be used to check {@link CacheEntryEvent}s prior to being
 * dispatched to {@link CacheEntryListener}s.
 * <p>
 * A filter must not create side effects.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 * @author Greg Luck
 * @author Brian Oliver
 * @since 1.0
 */
public interface CacheEntryEventFilter<K, V> {

  /**
   * Evaluates specified {@link CacheEntryEvent}.
   *
   * @param event the event that occurred
   * @return true if the evaluation passes, otherwise false.
   *         The effect of returning true is that listener will be invoked
   * @throws CacheEntryListenerException if there is problem executing the listener
   */
  boolean evaluate(CacheEntryEvent<? extends K, ? extends V> event)
      throws CacheEntryListenerException;
}

CacheEntryEventFilter 不需要和原始事件在同一进程,在分布式实现中,过滤器可以在可以提供最好性能的位置实现。

下表中总结了缓存操作可能触发的监听器。条件是触发缓存操作前调班的状态,过期总是不触发的,确切的过期时间由缓存实现来决定。

方法 创建 过期 删除 更新
boolean containsKey(K key) NO NO NO NO
V get(K key) Yes, if created by read-through No No No
Map<K,V> getAll(Collection<? extends K> keys) Yes, if created by read-through No No No
V getAndPut(K key, V value) if missing No No if there
V getAndRemove(K key) No No if there No
V getAndReplace(K key, V value) No No No if there
<T> T invoke(K key, EntryProcessor<K, V> entryProcessor); Yes, if setValue() created an entry, or getValue() created an entry by read-through No Yes, if remove() did remove an entry Yes, if setValue() updated an entry
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments); Yes, if setValue() created an entry, or getValue() created an entry by read-through No Yes, if remove() did remove an entry Yes, if setValue() updated an entry
Iterator<Cache.Entry<K, V>> iterator() No No Yes, if remove() did remove an entry No
void loadAll(Set<? extends K> keys,boolean replaceExistingValues, CompletionListener completionListener); if missing No No if there
void put(K key, V value) if missing No No if there
void putAll(Map<? extends K,? extends V> map) if missing No No if there
boolean putIfAbsent(K key, V value) if missing No No No
boolean remove(K key) No No if there No
boolean remove(K key, V oldValue) No No if there && equal No
void removeAll() No No if there No
void removeAll(Set<? extends K> keys) No No if there No
boolean replace(K key, V value) No No No if there
boolean replace(K key, V oldValue, V newValue) No No No if there && equal

Entry Processors

javax.cache.processor.EntryProcessor 是一个可调用函数,类似于 java.util.concurrent.Callable ,它提供了一种有效执行复合缓存操作的一种方式,不通过事务或显式加锁来原子性的访问,更新,删除指定的 key。

使用 Cache.invokeCache.invokeAll 时, MutableEntry 会作为入参传递给 EntryProcessor ,此时该处理器拥有该实体的排他访问权。

MutableEntry 接口定义如下:

package javax.cache.processor;

import javax.cache.Cache;
import javax.cache.integration.CacheLoader;

/**
 * A mutable representation of a {@link javax.cache.Cache.Entry}.
 * <p>
 * Mutable entries are used by {@link EntryProcessor}s to mutate
 * {@link Cache.Entry}s in place, atomically.
 *
 * @param <K> the type of key
 * @param <V> the type of value
 *
 * @author Greg Luck
 * @since 1.0
 * @see EntryProcessor
 */
public interface MutableEntry<K, V> extends Cache.Entry<K, V> {

  /**
   * Checks for the existence of the entry in the cache
   *
   * @return true if the entry exists
   */
  boolean exists();

  /**
   * Removes the entry from the Cache.
   * <p>
   * This has the same semantics as calling {@link Cache#remove}.
   */
  void remove();

  /**
   * Returns the value stored in the cache.
   * <p>
   * If the cache is configured to use read-through, and this method
   * would return null because the entry is missing from the cache,
   * the Cache's {@link CacheLoader} is called in an attempt to load
   * the entry.
   *
   * @return the value corresponding to this entry
   */
  V getValue();

  /**
   * Sets or replaces the value associated with the key.
   * <p>
   * If {@link #exists} is false and setValue is called
   * then a mapping is added to the cache visible once the EntryProcessor
   * completes. Moreover a second invocation of {@link #exists()}
   * will return true.
   *
   * @param value the value to update the entry with
   * @throws ClassCastException if the implementation supports and is
   *                            configured to perform runtime-type-checking,
   *                            and value type is incompatible with that
   *                            which has been configured for the
   *                            {@link Cache}
   */
  void setValue(V value);
}

应用可能想要原子的检查条目的值,计算新值,更新条目并返回其他值,可以考虑使用 EntryProcessor 来实现这个操作。

javax.cache.processor.EntryProcessor 接口定义如下:

package javax.cache.processor;

import javax.cache.Cache;
import javax.cache.event.CacheEntryListener;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheWriter;


/**
 * An invocable function that allows applications to perform compound operations
 * on a {@link javax.cache.Cache.Entry} atomically, according to the defined
 * consistency of a {@link Cache}.
 * <p>
 * Any {@link javax.cache.Cache.Entry} mutations will not take effect until after
 * the {@link EntryProcessor#process(MutableEntry, Object...)} method has completed
 * execution.
 * <p>
 * If an exception is thrown by an {@link EntryProcessor}, a Caching Implementation
 * must wrap any {@link Exception} thrown wrapped in an {@link
 * EntryProcessorException}. If this occurs no mutations will be made to the
 * {@link javax.cache.Cache.Entry}.
 * <p>
 * Implementations may execute {@link EntryProcessor}s in situ, thus avoiding
 * locking, round-trips and expensive network transfers.
 *
 * <h3>Effect of {@link MutableEntry} operations</h3>
 * {@link javax.cache.Cache.Entry} access, via a call to
 * {@link javax.cache.Cache.Entry#getValue()}, will behave as if
 * {@link Cache#get(Object)} was called for the key.  This includes updating
 * necessary statistics, consulting the configured {@link ExpiryPolicy} and loading
 * from a configured {@link javax.cache.integration.CacheLoader}.
 * <p>
 * {@link javax.cache.Cache.Entry} mutation, via a call to
 * {@link MutableEntry#setValue(Object)}, will behave as if {@link
 * Cache#put(Object, Object)} was called for the key. This includes updating
 * necessary statistics, consulting the configured {@link
 * ExpiryPolicy}, notifying {@link CacheEntryListener}s and writing to a
 * configured {@link CacheWriter}.
 * <p>
 * {@link javax.cache.Cache.Entry} removal, via a call to
 * {@link MutableEntry#remove()}, will behave as if {@link Cache#remove(Object)}
 * was called for the key. This includes updating necessary statistics, notifying
 * {@link CacheEntryListener}s and causing a delete on a configured
 * {@link CacheWriter}.
 * <p>
 * As implementations may choose to execute {@link EntryProcessor}s remotely,
 * {@link EntryProcessor}s, together with specified parameters and return
 * values, may be required to implement {@link java.io.Serializable}.
 *
 * <h3>Effect of multiple {@link MutableEntry} operations performed by one {@link
 * EntryProcessor}</h3>
 * Only the net effect of multiple operations has visibility outside of the Entry
 * Processor. The entry is locked by the entry processor for the entire scope
 * of the entry processor, so intermediate effects are not visible.
 * <h4>Example 1</h4>
 * In this example, an {@link EntryProcessor} calls:
 * <ol>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#setValue(Object)}</li>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#setValue(Object)}</li>
 * </ol>
 * This will have the following {@link Cache} effects:
 * <br>
 * Final value of the cache: last setValue<br>
 * Statistics: one get and one put as the second get and the first put are
 * internal to the EntryProcessor.<br>
 * Listeners: second put will cause either a put or an update depending on whether
 * there was an initial value for the entry.<br>
 * CacheLoader: Invoked by the first get only if the entry is not present, a
 * loader was registered and read through is enabled.<br>
 * CacheWriter: Invoked by the second put only as the first put was internal to
 * the Entry Processor.<br>
 * ExpiryPolicy: The first get and the second put only are visible to the
 * ExpiryPolicy.<br>
 *
 * <h4>Example 2</h4>
 * In this example, an {@link EntryProcessor} calls:
 * <ol>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#remove()}}</li>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#setValue(Object)}</li>
 * </ol>
 * This will have the following {@link Cache} effects:
 * <br>
 * Final value of the cache: last setValue<br>
 * Statistics: one get and one put as the second get and the first put are
 * internal to the EntryProcessor.<br>
 * Listeners: second put will cause either a put or an update depending on whether
 * there was an initial value for the entry.<br>
 * CacheLoader: Invoked by the first get only if the entry is not present, a loader
 * was registered and read through is enabled.<br>
 * CacheWriter: Invoked by the second put only as the first put was internal to
 * the Entry Processor.<br>
 * ExpiryPolicy: The first get and the second put only are visible to the
 * ExpiryPolicy.<br>
 *
 * <h4>Example 3</h4>
 * In this example, an {@link EntryProcessor} calls:
 * <ol>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#setValue(Object)}}</li>
 * <li>{@link MutableEntry#getValue()}</li>
 * <li>{@link MutableEntry#setValue(Object)}</li>
 * <li>{@link MutableEntry#remove()}</li>
 * </ol>
 * This will have the following {@link Cache} effects:
 * <br>
 * Final value of the cache: the entry is removed if it was present<br>
 * Statistics: one get and one remove as the second get and the two puts are
 * internal to the EntryProcessor.<br>
 * Listeners: remove if there was initial value in the cache, otherwise no
 * listener invoked.
 * <br> CacheLoader: Invoked by the first get only if the entry is not present,
 * a loader was registered and read through is enabled.
 * <br> CacheWriter: Invoked by the remove only as the two puts are internal to
 * the Entry Processor, provided that the first #getValue was non-null.<br>
 * ExpiryPolicy: The first get only is visible to the ExpiryPolicy. There is no
 * remove event in ExpiryPolicy.
 *
 * @param <K> the type of keys maintained by this cache
 * @param <V> the type of cached values
 * @param <T> the type of the return value
 * @author Greg Luck
 * @since 1.0
 */
public interface EntryProcessor<K, V, T> {

  /**
   * Process an entry.
   *
   * @param entry     the entry
   * @param arguments a number of arguments to the process.
   * @return the user-defined result of the processing, if any.
   * @throws EntryProcessorException if there is a failure in entry processing.
   */
  T process(MutableEntry<K, V> entry, Object... arguments)
      throws EntryProcessorException;
}

为了对指定条目使用 EntryProcessor ,应用必须使用 Cache.invokeCache.invokeAll 方法。

/**
 * Invokes an {@link EntryProcessor} against the {@link Entry} specified by
 * the provided key.
 *
 * @param <T>            the type of the return value
 * @param key            the key to the entry
 * @param entryProcessor the {@link EntryProcessor} to invoke
 * @param arguments      additional arguments to pass to the
 *                       {@link EntryProcessor}
 * @return the result of the processing, if any, defined by the
 *         {@link EntryProcessor} implementation
 * @throws NullPointerException    if key or {@link EntryProcessor} is null
 * @throws IllegalStateException   if the cache is {@link #isClosed()}
 * @throws ClassCastException    if the implementation is configured to perform
 *                               runtime-type-checking, and the key or value
 *                               types are incompatible with those that have been
 *                               configured for the {@link Cache}
 * @throws EntryProcessorException if an exception is thrown by the {@link
 *                                 EntryProcessor}, a Caching Implementation
 *                                 must wrap any {@link Exception} thrown
 *                                 wrapped in an {@link EntryProcessorException}.
 * @see EntryProcessor
 */
<T> T invoke(K key,
             EntryProcessor<K, V, T> entryProcessor,
             Object... arguments) throws EntryProcessorException;
/**
 * Invokes an {@link EntryProcessor} against the set of {@link Entry}s
 * specified by the set of keys.
 * <p>
 * The order that the entries for the keys are processed is undefined.
 * Implementations may choose to process the entries in any order, including
 * concurrently.  Furthermore there is no guarantee implementations will
 * use the same {@link EntryProcessor} instance to process each entry, as
 * the case may be in a non-local cache topology.
 * <p>
 * The result of executing the {@link EntryProcessor} is returned as a
 * {@link Map} of {@link EntryProcessorResult}s, one result per key.  Should the
 * {@link EntryProcessor} or Caching implementation throw an exception, the
 * exception is wrapped and re-thrown when a call to
 * {@link javax.cache.processor.EntryProcessorResult#get()} is made.
 *
 * @param <T>            the type of the return value
 * @param keys           the set of keys for entries to process
 * @param entryProcessor the {@link EntryProcessor} to invoke
 * @param arguments      additional arguments to pass to the
 *                       {@link EntryProcessor}
 * @return the map of {@link EntryProcessorResult}s of the processing per key,
 * if any, defined by the {@link EntryProcessor} implementation.  No mappings
 * will be returned for {@link EntryProcessor}s that return a
 * <code>null</code> value for a key.
 * @throws NullPointerException    if keys or {@link EntryProcessor} are null
 * @throws IllegalStateException   if the cache is {@link #isClosed()}
 * @throws ClassCastException    if the implementation is configured to perform
 *                               runtime-type-checking, and the key or value
 *                               types are incompatible with those that have been
 *                               configured for the {@link Cache}
 * @see EntryProcessor
 */
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,
                                              EntryProcessor<K, V, T> entryProcessor,
                                              Object... arguments);

下面是一个例子

CachingProvider provider = Caching.getCachingProvider();
CacheManager manager = provider.getCacheManager();

MutableConfiguration<String, Integer> configuration =
  new MutableConfiguration<String, Integer>()
    .setTypes(String.class, Integer.class);

Cache<String, Integer> cache = manager.createCache("example", configuration);

String key = "counter";
cache.put(key, 1);

int previous = cache.invoke(key, new IncrementProcessor<String>());

assert previous == 1;
assert cache.get(key) == 2;
/**
 * An {@link EntryProcessor} that increments an {@link Integer}.
 *
 * @param <K> the type of keys
 */
public static class IncrementProcessor<K> implements EntryProcessor<K,
    Integer, Integer>, Serializable {

  /**
   * The serialVersionUID required for {@link java.io.Serializable}.
   */
  public static final long serialVersionUID = 201306211238L;

  /**
   * {@inheritDoc}
   */
  @Override
  public Integer process(MutableEntry<K, Integer> entry, Object... arguments) {
    if (entry.exists()) {
      Integer current = entry.getValue();
      entry.setValue(current + 1);
      return current;
    } else {
      entry.setValue(0);
      return -1;
    }
  }
}

支持远程或分布式缓存拓扑的实现可以选择在远程进程中执行 EntryProcessor 。 在这种情况下,实现可能需要 EntryProcessor ,调用参数和返回类型来实现 java.lang.Serializable 或以某种方式可序列化。 或者,实现可以选择简单地将 EntryProcessor 类名与指定的调用参数一起序列化,并通过实例化 EntryProcessor 类和使用反序列化的调用参数进行来远程执行请求。

由于 EntryProcessor 的结果是原子的,因此与 CacheLoaderCacheWriterCacheEntryListenerExpiryPolicy 的交互也是如此。

在调用 EntryProcessor 时,应用程序永远不会观察到对 MutableEntry getValuesetValueremove 等的单独调用的中间事件或副作用。 相反,应用程序将仅观察由 EntryProcessorCache.Entry 执行的操作的“净”结果。

例如,一个 EntryProcessor 有如下操作:

V v1 = entry.getValue();
entry.setValue(v2);
entry.remove();
entry.setValue(v3);
v4 = entry.getValue();

仅会产生一个从 v1 -> V3 的更新事件。

EntryProcessor 中的异常

如果通过缓存实现或 EntryProcessor 本身在调用 EntryProcessor 期间引发异常,则必须将异常包装为 javax.cache.processor.EntryProcessorException 返回给调用应用程序。

当在使用 Cache.invoke 方法调用 EntryProcessor 的过程中发生异常时,该异常将包装为 EntryProcessorException 并重新抛出给调用应用程序。

当使用 Cache.invokeAll 方法调用 EntryProcessor 时发生异常时,该异常将被包装并作为 EntryProcessorResult 的一部分返回给调用应用程序。

Cache.invokeAll 中的 EntryProcessorResults

当使用 Cache.invokeAll 方法针对一组键调用 EntryProcessor 时,结果为 EntryProcessorResultMap ,每个键一个(除非对键的 EntryProcessor 调用结果为 null )。

若要检索 EntryProcessor 为特定键返回的值,应用程序应为该键的 EntryProcessorResult 调用 EntryProcessorResult.get 方法。 如果 EntryProcessor 在处理 key 时产生了的异常,则调用 EntryProcessorResult.get 将引发相应的封装异常。

CachingProvider

CachingProvider 是 Java Caching API 里的核心概念。开发者可以通过 CachingProvider 来获取 CacheManager 以便可以与 Cache 进行交互。

CachingProvider 提供了以下特性:

  • 获取默认的 CacheManager 实例
  • 创建 CacheManager 实例,由特定的URI唯一标识。例如 一个实现可能会请求在类路径上声明式的配置了配置文件的 CacheManager ,
    • cachingProvider.getCacheManager(“/sample/ConfigurationFile.xml”);
  • 通过 URIClassLoader 范围和管理 CacheManager 实例
  • 关闭并释放特定的或相关的 CacheManager 集合
  • 查询 CachingProvider 实现的功能,包括对可选功能的支持。

CacheManager 唯一标识和配置

  • CacheManagerCachingProvider 范围内,由 URI 唯一标识。

CachingProvider 提供的使用默认URI和默认 ClassLoader 创建的 CacheManager 称为“默认缓存管理器”。 它是通过 CacheManager getCacheManager(); 获得的。

尽管应用程序通常使用由 CachingProvider 定义的默认URI作为获取 CacheManager 的方式,但应用程序可能会另外使用特定于实现的URI来进行 CacheManager 的高级配置。

例如,一种实现可以允许将URI用作配置文件的位置,例如用于预配置的缓存。

cachingProvider.getCacheManager(“/sample/ConfigurationFile.xml”;

在应用程序部署中使用相同的URI和相同的 ClassLoader 定义的两个或多个 CacheManager 在逻辑上是等效的,并且必须管理相同的Cache。

用于获取 CacheManager 的URI定义的缓存的语义与实现有关。

例如:两个或更多使用相同URI且具有支持分布式缓存的实现的应用程序可以在逻辑上共享同名缓存的缓存内容。 在这种情况下,对一个应用程序中的缓存条目的更改将对另一应用程序可见。

或者,即使使用相同的缓存名称,使用相同URI且仅支持本地缓存的实现的两个或多个应用程序也可能无法共享缓存内容。 在这种情况下,对一个应用程序中的缓存条目的更改可能对另一应用程序不可见。

下表概述了CacheManager URI如何使用仅支持本地(非共享)缓存和分布式(或共享)缓存的实现方式影响同名缓存中的缓存条目的可见性。

| CacheManager URI|本地缓存|分布式缓存| |相同|相同的配置,但是条目不同|相同配置,相同实体| |不同|不同配置,不同实体|不同配置,不同实体|

CachingProvider 接口定义如下:

package javax.cache.spi;

import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.configuration.OptionalFeature;
import java.io.Closeable;
import java.net.URI;
import java.util.Properties;

/**
 * Provides mechanisms to create, request and later manage the life-cycle of
 * configured {@link CacheManager}s, identified by {@link URI}s and scoped by
 * {@link ClassLoader}s.
 * <p>
 * The meaning and semantics of the {@link URI} used to identify a
 * {@link CacheManager} is implementation dependent.  For applications to remain
 * implementation independent, they should avoid attempting to create {@link URI}s
 * and instead use those returned by {@link #getDefaultURI()}.
 *
 * @author Brian Oliver
 * @author Greg Luck
 * @since 1.0
 */
public interface CachingProvider extends Closeable {

  /**
   * Requests a {@link CacheManager} configured according to the implementation
   * specific {@link URI} be made available that uses the provided
   * {@link ClassLoader} for loading underlying classes.
   * <p>
   * Multiple calls to this method with the same {@link URI} and
   * {@link ClassLoader} must return the same {@link CacheManager} instance,
   * except if a previously returned {@link CacheManager} has been closed.
   * <p>
   * Properties are used in construction of a {@link CacheManager} and do not form
   * part of the identity of the CacheManager. i.e. if a second call is made to
   * with the same {@link URI} and {@link ClassLoader} but different properties,
   * the {@link CacheManager} created in the first call is returned.
   * <p>
   * Properties names follow the same scheme as package names.
   * The prefixes {@code java} and {@code javax} are reserved.
   * Properties are passed through and can be retrieved via
   * {@link CacheManager#getProperties()}.
   * Properties within the package scope of a caching implementation may be used for
   * additional configuration.
   *
   * @param uri         an implementation specific URI for the
   *                    {@link CacheManager} (null means use
   *                    {@link #getDefaultURI()})
   * @param classLoader the {@link ClassLoader}  to use for the
   *                    {@link CacheManager} (null means use
   *                    {@link #getDefaultClassLoader()})
   * @param properties  the {@link Properties} for the {@link CachingProvider}
   *                    to create the {@link CacheManager} (null means no
   *                    implementation specific Properties are required)
   * @throws CacheException    when a {@link CacheManager} for the
   *                           specified arguments could not be produced
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  CacheManager getCacheManager(URI uri, ClassLoader classLoader,
                               Properties properties);

  /**
   * Obtains the default {@link ClassLoader} that will be used by the
   * {@link CachingProvider}.
   *
   * @return the default {@link ClassLoader} used by the {@link CachingProvider}
   */
  ClassLoader getDefaultClassLoader();

  /**
   * Obtains the default {@link URI} for the {@link CachingProvider}.
   * <p>
   * Use this method to obtain a suitable {@link URI} for the
   * {@link CachingProvider}.
   *
   * @return the default {@link URI} for the {@link CachingProvider}
   */
  URI getDefaultURI();

  /**
   * Obtains the default {@link Properties} for the {@link CachingProvider}.
   * <p>
   * Use this method to obtain suitable {@link Properties} for the
   * {@link CachingProvider}.
   *
   * @return the default {@link Properties} for the {@link CachingProvider}
   */
  Properties getDefaultProperties();

  /**
   * Requests a {@link CacheManager} configured according to the implementation
   * specific {@link URI} that uses the provided {@link ClassLoader} for loading
   * underlying classes.
   * <p>
   * Multiple calls to this method with the same {@link URI} and
   * {@link ClassLoader} must return the same {@link CacheManager} instance,
   * except if a previously returned {@link CacheManager} has been closed.
   *
   * @param uri         an implementation specific {@link URI} for the
   *                    {@link CacheManager} (null means
   *                    use {@link #getDefaultURI()})
   * @param classLoader the {@link ClassLoader}  to use for the
   *                    {@link CacheManager} (null means
   *                    use {@link #getDefaultClassLoader()})
   * @throws CacheException    when a {@link CacheManager} for the
   *                           specified arguments could not be produced
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  CacheManager getCacheManager(URI uri, ClassLoader classLoader);

  /**
   * Requests a {@link CacheManager} configured according to the
   * {@link #getDefaultURI()} and {@link #getDefaultProperties()} be made
   * available that using the {@link #getDefaultClassLoader()} for loading
   * underlying classes.
   * <p>
   * Multiple calls to this method must return the same {@link CacheManager}
   * instance, except if a previously returned {@link CacheManager} has been
   * closed.
   *
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  CacheManager getCacheManager();

  /**
   * Closes all of the {@link CacheManager} instances and associated resources
   * created and maintained by the {@link CachingProvider} across all
   * {@link ClassLoader}s.
   * <p>
   * After closing the {@link CachingProvider} will still be operational.  It
   * may still be used for acquiring {@link CacheManager} instances, though
   * those will now be new.
   *
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  void close();

  /**
   * Closes all {@link CacheManager} instances and associated resources created
   * by the {@link CachingProvider} using the specified {@link ClassLoader}.
   * <p>
   * After closing the {@link CachingProvider} will still be operational.  It
   * may still be used for acquiring {@link CacheManager} instances, though
   * those will now be new for the specified {@link ClassLoader} .
   *
   * @param classLoader the {@link ClassLoader}  to release
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  void close(ClassLoader classLoader);

  /**
   * Closes all {@link CacheManager} instances and associated resources created
   * by the {@link CachingProvider} for the specified {@link URI} and
   * {@link ClassLoader}.
   *
   * @param uri         the {@link URI} to release
   * @param classLoader the {@link ClassLoader}  to release
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  void close(URI uri, ClassLoader classLoader);

  /**
   * Determines whether an optional feature is supported by the
   * {@link CachingProvider}.
   *
   * @param optionalFeature the feature to check for
   * @return true if the feature is supported
   */
  boolean isSupported(OptionalFeature optionalFeature);
}

尽管是可选的,在 Java SE 环境中获取 CachingProvider 的主要途径是 Caching 类。

Caching 类提供了三种方式来加载和实例化 CachingProvider

java.util.ServiceLoader
javax.cache.CachingProvider
CachingProvider

尽管开发人员可以选择使用依赖于实现的方式来获取 CachingProvider ,但这样做可能会降低可移植性。

为了使 CachingProvider 实现由 Caching 类通过 java.util.ServiceLoader 自动定位,必须以 Jar 文件规范中定义的格式,在 META-INF/services/javax.cache.spi.CachingProvider 中定义 CachingProvider 实现的全类名。

javax.cache.spi.CachingProvider 配置文件用于为 Caching 类定义特定的 CachingProvider 实现类,从而允许它根据请求自动定位,加载并提供适当的实例给应用程序。

javax.cache.spi.CachingProvider 配置文件的内容只是一个或多个全类名,每个类名都位于单独的行中,每个类名都是可用的 CachingProvider 实现。

例如: Java Caching API 实现程序 ACME Caching Products 附带了一个名为 acme.jar 的 JAR,其中包含 CachingProvider 实现。 JAR的内容包括 CachingProvider 实现和 javax.cache.spi.CachingProvider 配置文件。

META-INF/services/javax.cache.spi.CachingProvider
com/acme/cache/ACMECachingProvider.class
...

META-INF/services/javax.cache.spi.CachingProvider 的内容只不过是实现类的名称: com.acme.cache.ACMECachingProvider

通过正确配置 META-INF/services/javax.cache.spi.CachingProvider 文件,应用程序可以使用多个 CachingProvider 实现。 当有多个 CachingProvider 可用时,从 Caching 类返回默认 CachingProvider 的请求将导致异常。

Caching 类定义如下:

package javax.cache;

import javax.cache.spi.CachingProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.ServiceLoader;
import java.util.WeakHashMap;

/**
 * The {@link Caching} class provides a convenient means for an application to
 * acquire an appropriate {@link CachingProvider} implementation.
 * <p>
 * While defined as part of the specification, its use is not required.
 * Applications and/or containers may instead choose to directly instantiate a
 * {@link CachingProvider} implementation based on implementation specific
 * instructions.
 * <p>
 * When using the {@link Caching} class, {@link CachingProvider} implementations
 * are automatically discovered when they follow the conventions outlined by the
 * Java Development Kit {@link ServiceLoader} class.
 * <p>
 * Although automatically discovered, applications that choose to use this class
 * should not make assumptions regarding the order in which implementations are
 * returned by the {@link #getCachingProviders()} or
 * {@link #getCachingProviders(ClassLoader)} methods.
 * <p>
 * For a {@link CachingProvider} to be automatically discoverable by the
 * {@link Caching} class, the fully qualified class name of the
 * {@link CachingProvider} implementation must be declared in the following
 * file:
 * <pre>
 *   META-INF/services/javax.cache.spi.CachingProvider
 * </pre>
 * This file must be resolvable via the class path.
 * <p>
 * For example, in the reference implementation the contents of this file are:
 * <code>org.jsr107.ri.RICachingProvider</code>
 * <p>
 * Alternatively when the fully qualified class name of a
 * {@link CachingProvider} implementation is specified using the system property
 * <code>javax.cache.spi.cachingprovider</code>, that implementation will be used
 * as the default {@link CachingProvider}.
 * <p>
 * All {@link CachingProvider}s that are automatically detected or explicitly
 * declared and loaded by the {@link Caching} class are maintained in an
 * internal registry.  Consequently when a previously loaded
 * {@link CachingProvider} is requested, it will be simply returned from the
 * internal registry, without reloading and/or instantiating the said
 * implementation again.
 * <p>
 * As required by some applications and containers, multiple co-existing
 * {@link CachingProvider}s implementations, from the same or different
 * implementors are permitted at runtime.
 * <p>
 * To iterate through those that are currently registered a developer may use
 * the following methods:
 * <ol>
 * <li>{@link #getCachingProviders()}</li>
 * <li>{@link #getCachingProviders(ClassLoader)}</li>
 * </ol>
 * To request a specific {@link CachingProvider} implementation, a developer
 * should use either the {@link #getCachingProvider(String)} or
 * {@link #getCachingProvider(String, ClassLoader)} method.
 * <p>
 * Where multiple {@link CachingProvider}s are present, the
 * {@link CachingProvider} returned by getters {@link #getCachingProvider()} and
 * {@link #getCachingProvider(ClassLoader)} is undefined and as a result a
 * {@link CacheException} will be thrown when attempted.
 *
 * @author Brian Oliver
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @since 1.0
 * @see ServiceLoader
 * @see CachingProvider
 */
public final class Caching {

  /**
   * The <code>javax.cache.spi.cachingprovider</code> constant.
   */
  public static final String JAVAX_CACHE_CACHING_PROVIDER = "javax.cache" +
      ".spi.CachingProvider";

  /**
   * The {@link CachingProviderRegistry} that tracks the {@link CachingProvider}s.
   */
  private static final CachingProviderRegistry CACHING_PROVIDERS =
      new CachingProviderRegistry();

  /**
   * No public constructor as all methods are static.
   */
  private Caching() {
  }

  /**
   * Obtains the {@link ClassLoader} to use for API methods that don't
   * explicitly require a {@link ClassLoader} but internally require one.
   * <p>
   * By default this is the {@link Thread#getContextClassLoader()}.
   *
   * @return the default {@link ClassLoader}
   */
  public static ClassLoader getDefaultClassLoader() {
    return CACHING_PROVIDERS.getDefaultClassLoader();
  }

  /**
   * Set the {@link ClassLoader} to use for API methods that don't explicitly
   * require a {@link ClassLoader}, but internally use one.
   *
   * @param classLoader the {@link ClassLoader} or <code>null</code> if the
   *                    calling {@link Thread#getContextClassLoader()} should
   *                    be used
   */
  public static void setDefaultClassLoader(ClassLoader classLoader) {
    CACHING_PROVIDERS.setDefaultClassLoader(classLoader);
  }

  /**
   * Obtains the default {@link CachingProvider} available via the
   * {@link #getDefaultClassLoader()}.
   *
   * @return the {@link CachingProvider}
   * @throws CacheException    should zero, or more than one
   *                           {@link CachingProvider} be available on the
   *                           classpath, or it could not be loaded
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  public static CachingProvider getCachingProvider() {
    return CACHING_PROVIDERS.getCachingProvider();
  }

  /**
   * Obtains the single {@link CachingProvider} visible to the specified
   * {@link ClassLoader}.
   *
   * @param classLoader the {@link ClassLoader} to use for loading the
   *                    {@link CachingProvider}
   * @return the {@link CachingProvider}
   * @throws CacheException    should zero, or more than one
   *                           {@link CachingProvider} be available on the
   *                           classpath, or it could not be loaded
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   * @see #getCachingProviders(ClassLoader)
   */
  public static CachingProvider getCachingProvider(ClassLoader classLoader) {
    return CACHING_PROVIDERS.getCachingProvider(classLoader);
  }

  /**
   * Obtains the {@link CachingProvider}s that are available via the
   * {@link #getDefaultClassLoader()}.
   * <p>
   * If a <code>javax.cache.spi.cachingprovider</code> system property is defined,
   * only that {@link CachingProvider} specified by that property is returned.
   * Otherwise all {@link CachingProvider}s that are available via a
   * {@link ServiceLoader} for {@link CachingProvider}s using the default
   * {@link ClassLoader} (including those previously requested via
   * {@link #getCachingProvider(String)}) are returned.
   *
   * @return an {@link Iterable} of {@link CachingProvider}s loaded by the
   *         specified {@link ClassLoader}
   */
  public static Iterable<CachingProvider> getCachingProviders() {
    return CACHING_PROVIDERS.getCachingProviders();
  }

  /**
   * Obtains the {@link CachingProvider}s that are available via the specified
   * {@link ClassLoader}.
   * <p>
   * If a <code>javax.cache.spi.cachingprovider</code> system property is defined,
   * only that {@link CachingProvider} specified by that property is returned.
   * Otherwise all {@link CachingProvider}s that are available via a
   * {@link ServiceLoader} for {@link CachingProvider}s using the specified
   * {@link ClassLoader} (including those previously requested via
   * {@link #getCachingProvider(String, ClassLoader)}) are returned.
   *
   * @param classLoader the {@link ClassLoader} of the returned
   *                    {@link CachingProvider}s
   * @return an {@link Iterable} of {@link CachingProvider}s loaded by the
   *         specified {@link ClassLoader}
   */
  public static Iterable<CachingProvider> getCachingProviders(
      ClassLoader classLoader) {
    return CACHING_PROVIDERS.getCachingProviders(classLoader);
  }

  /**
   * Obtain the {@link CachingProvider} that is implemented by the specified
   * fully qualified class name using the {@link #getDefaultClassLoader()}.
   * Should this {@link CachingProvider} already be loaded it is simply returned,
   * otherwise an attempt will be made to load and instantiate the specified
   * class (using a no-args constructor).
   *
   * @param fullyQualifiedClassName the fully qualified class name of the
   *                                {@link CachingProvider}
   * @return the {@link CachingProvider}
   * @throws CacheException    if the {@link CachingProvider} cannot be created
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  public static CachingProvider getCachingProvider(String fullyQualifiedClassName) {
    return CACHING_PROVIDERS.getCachingProvider(fullyQualifiedClassName);
  }

  /**
   * Obtain the {@link CachingProvider} that is implemented by the specified
   * fully qualified class name using the provided {@link ClassLoader}.
   * Should this {@link CachingProvider} already be loaded it is returned,
   * otherwise an attempt will be made to load and instantiate the specified
   * class (using a no-args constructor).
   *
   * @param fullyQualifiedClassName the fully qualified class name of the
   *                                {@link CachingProvider}
   * @param classLoader             the {@link ClassLoader} to load the
   *                                {@link CachingProvider}
   * @return the {@link CachingProvider}
   * @throws CacheException    if the {@link CachingProvider} cannot be created
   * @throws SecurityException when the operation could not be performed
   *                           due to the current security settings
   */
  public static CachingProvider getCachingProvider(String fullyQualifiedClassName,
                                                   ClassLoader classLoader) {
    return CACHING_PROVIDERS.getCachingProvider(fullyQualifiedClassName,
        classLoader);
  }


  /**
   * A convenience that method that looks up a managed {@link Cache} given its
   * name. using the default <code>CachingProvider</code> and <code>CacheManager
   * </code>. For the full range of <code>Cache</code> look up methods see
   * {@link CacheManager}.
   * <p>
   * This method must be used for {@link Cache}s that were configured with
   * runtime key and value types. Use {@link CacheManager#getCache(String)} for
   * {@link Cache}s where these were not specified.
   * <p>
   * Implementations must ensure that the key and value types are the same as
   * those configured for the {@link Cache} prior to returning from this method.
   * <p>
   * Implementations may further perform type checking on mutative cache operations
   * and throw a {@link ClassCastException} if these checks fail.
   * <p>
   * Implementations that support declarative mechanisms for pre-configuring
   * {@link Cache}s may return a pre-configured {@link Cache} instead of
   * <code>null</code>.
   * @param <K> the type of key
   * @param <V> the type of value
   * @param cacheName the name of the managed {@link Cache} to acquire
   * @param keyType   the expected {@link Class} of the key
   * @param valueType the expected {@link Class} of the value
   * @return the Cache or null if it does exist or can't be pre-configured
   * @throws IllegalStateException    if the CacheManager is
   *                                  {@link CacheManager#isClosed()}
   * @throws IllegalArgumentException if the specified key and/or value types are
   *                                  incompatible with the configured cache.
   * @throws SecurityException        when the operation could not be performed
   *                                  due to the current security settings
   * @see CacheManager#getCache(String, Class, Class)
   * @see CacheManager#getCache(String)
   */
  public static <K, V> Cache<K, V> getCache(String cacheName, Class<K> keyType,
                              Class<V> valueType) {

    return getCachingProvider().getCacheManager().getCache(cacheName, keyType,
        valueType);
  }

  /**
   * Maintains a registry of loaded {@link CachingProvider}s scoped by
   * {@link ClassLoader}.
   */
  private static class CachingProviderRegistry {

    /**
     * The {@link CachingProvider}s by Class Name organized by the
     * {@link ClassLoader} was used to load them.
     */
    private WeakHashMap<ClassLoader, LinkedHashMap<String, CachingProvider>>
        cachingProviders;

    /**
     * The default {@link ClassLoader}.  When <code>null</code> the
     * {@link Thread#getContextClassLoader()} will be used.
     */
    private volatile ClassLoader classLoader;

    /**
     * Constructs a CachingProviderManager.
     */
    public CachingProviderRegistry() {
      this.cachingProviders = new WeakHashMap<ClassLoader, LinkedHashMap<String,
          CachingProvider>>();
      this.classLoader = null;
    }

    /**
     * Obtains the {@link ClassLoader} to use for API methods that don't
     * explicitly require a {@link ClassLoader} but internally require one.
     * <p>
     * By default this is the {@link Thread#getContextClassLoader()}.
     * </p>
     * @return the default {@link ClassLoader}
     */
    public ClassLoader getDefaultClassLoader() {
      ClassLoader loader = classLoader;
      return loader == null ? Thread.currentThread().getContextClassLoader() : loader;
    }

    /**
     * Set the {@link ClassLoader} to use for API methods that don't explicitly
     * require a {@link ClassLoader}, but internally use one.
     *
     * @param classLoader the {@link ClassLoader} or <code>null</code> if the
     *                    calling {@link Thread#getContextClassLoader()} should
     *                    be used
     */
    public void setDefaultClassLoader(ClassLoader classLoader) {
      this.classLoader = classLoader;
    }

    /**
     * Obtains the only {@link CachingProvider} defined by the
     * {@link #getDefaultClassLoader()}.
     * <p>
     * Should zero or more than one {@link CachingProvider}s be available, a
     * CacheException is thrown.
     * </p>
     * @return the {@link CachingProvider}
     * @throws CacheException should zero or more than one
     *                        {@link CachingProvider} be available
     *                        or a {@link CachingProvider} could not be loaded
     * @see #getCachingProvider(ClassLoader)
     * @see #getCachingProviders(ClassLoader)
     */
    public CachingProvider getCachingProvider() {
      return getCachingProvider(getDefaultClassLoader());
    }

    /**
     * Obtains the only {@link CachingProvider} defined by the specified
     * {@link ClassLoader}.
     * <p>
     * Should zero or more than one {@link CachingProvider}s be available, a
     * CacheException is thrown.
     * </p>
     * @param classLoader the {@link ClassLoader} to use for loading the
     *                    {@link CachingProvider}
     * @return the {@link CachingProvider}
     * @throws CacheException should zero or more than one
     *                        {@link CachingProvider} be available
     *                        or a {@link CachingProvider} could not be loaded
     * @see #getCachingProviders(ClassLoader)
     */
    public CachingProvider getCachingProvider(ClassLoader classLoader) {
      Iterator<CachingProvider> iterator = getCachingProviders(classLoader).iterator();

      if (iterator.hasNext()) {
        CachingProvider provider = iterator.next();

        if (iterator.hasNext()) {
          throw new CacheException("Multiple CachingProviders have been configured when only a single CachingProvider is expected");
        } else {
          return provider;
        }
      } else {
        throw new CacheException("No CachingProviders have been configured");
      }
    }

    /**
     * Obtain the {@link CachingProvider}s that are available via the
     * {@link #getDefaultClassLoader()}.
     * <p>
     * If a <code>javax.cache.spi.cachingprovider</code> system property is defined,
     * only that {@link CachingProvider} specified by that property is returned.
     * Otherwise all {@link CachingProvider}s that are available via a
     * {@link ServiceLoader} for {@link CachingProvider}s using the default
     * {@link ClassLoader} (and those explicitly requested via
     * {@link #getCachingProvider(String)}) are returned.
     * </p>
     * @return an {@link Iterable} of {@link CachingProvider}s loaded by the
     *         default {@link ClassLoader}
     */
    public Iterable<CachingProvider> getCachingProviders() {
      return getCachingProviders(getDefaultClassLoader());
    }

    /**
     * Obtain the {@link CachingProvider}s that are available via the specified
     * {@link ClassLoader}.
     * <p>
     * If a <code>javax.cache.spi.cachingprovider</code> system property is defined,
     * only that {@link CachingProvider} specified by that property is returned.
     * Otherwise all {@link CachingProvider}s that are available via a
     * {@link ServiceLoader} for {@link CachingProvider}s using the specified
     * {@link ClassLoader} (and those explicitly requested via
     * {@link #getCachingProvider(String, ClassLoader)}) are returned.
     * </p>
     * @param classLoader the {@link ClassLoader} of the returned
     *                    {@link CachingProvider}s
     * @return an {@link Iterable} of {@link CachingProvider}s loaded by the
     *         specified {@link ClassLoader}
     */
    public synchronized Iterable<CachingProvider> getCachingProviders(ClassLoader classLoader) {

      final ClassLoader serviceClassLoader = classLoader == null ? getDefaultClassLoader() : classLoader;
      LinkedHashMap<String, CachingProvider> providers = cachingProviders.get(serviceClassLoader);

      if (providers == null) {

        String className = System.getProperty(JAVAX_CACHE_CACHING_PROVIDER);
        if (className != null) {
          providers = new LinkedHashMap<String, CachingProvider>();
          providers.put(className, loadCachingProvider(className, serviceClassLoader));

        } else {
          providers = AccessController.doPrivileged(new PrivilegedAction<LinkedHashMap<String, CachingProvider>>() {
            @Override
            public LinkedHashMap<String, CachingProvider> run() {
              LinkedHashMap<String, CachingProvider> result = new LinkedHashMap<String, CachingProvider>();

              ServiceLoader<CachingProvider> serviceLoader = ServiceLoader.load(CachingProvider.class, serviceClassLoader);
              for (CachingProvider provider : serviceLoader) {
                result.put(provider.getClass().getName(), provider);
              }
              return result;
            }
          });

        }

        cachingProviders.put(serviceClassLoader, providers);
      }

      return providers.values();
    }

    /**
     * Obtain the {@link CachingProvider} that is implemented by the specified
     * fully qualified class name using the {@link #getDefaultClassLoader()}.
     * Should this {@link CachingProvider} already be loaded it is simply
     * returned, otherwise an attempt will be made to load and instantiate the
     * specified class name (using a no-args constructor).
     *
     * @param fullyQualifiedClassName the fully qualified class name of the
     *                                {@link CachingProvider}
     * @return the {@link CachingProvider}
     * @throws CacheException when the {@link CachingProvider} can't be created
     */
    public CachingProvider getCachingProvider(String fullyQualifiedClassName) {
      return getCachingProvider(fullyQualifiedClassName, getDefaultClassLoader());
    }

    /**
     * Load and instantiate the {@link CachingProvider} with the specified
     * fully qualified class name using the provided {@link ClassLoader}
     *
     * @param fullyQualifiedClassName the name of the {@link CachingProvider}
     *                                class
     * @param classLoader             the {@link ClassLoader} to use
     * @return a new {@link CachingProvider} instance
     * @throws CacheException if the specified {@link CachingProvider} could not be
     *                        loaded
     *                        or the specified class is not a {@link
     *                        CachingProvider}
     */
    protected CachingProvider loadCachingProvider(String fullyQualifiedClassName, ClassLoader classLoader) throws CacheException {
      synchronized (classLoader) {
        try {
          Class<?> clazz = classLoader.loadClass(fullyQualifiedClassName);
          if (CachingProvider.class.isAssignableFrom(clazz)) {
            return ((Class<CachingProvider>) clazz).newInstance();
          } else {
            throw new CacheException("The specified class [" + fullyQualifiedClassName + "] is not a CachingProvider");
          }
        } catch (Exception e) {
          throw new CacheException("Failed to load the CachingProvider [" + fullyQualifiedClassName + "]", e);
        }
      }
    }

    /**
     * Obtain the {@link CachingProvider} that is implemented by the specified
     * fully qualified class name using the provided {@link ClassLoader}.
     * Should this {@link CachingProvider} already be loaded it is returned,
     * otherwise an attempt will be made to load and instantiate the specified
     * class (using a no-args constructor).
     *
     * @param fullyQualifiedClassName the fully qualified class name of the
     *                                {@link CachingProvider}
     * @param classLoader             the {@link ClassLoader} to load the
     *                                {@link CachingProvider}
     * @return the {@link CachingProvider}
     * @throws CacheException when the {@link CachingProvider} can't be created
     */
    public synchronized CachingProvider getCachingProvider(String fullyQualifiedClassName, ClassLoader classLoader) {
      ClassLoader serviceClassLoader = classLoader == null ? getDefaultClassLoader() : classLoader;

      LinkedHashMap<String, CachingProvider> providers = cachingProviders.get(serviceClassLoader);

      if (providers == null) {
        // first load the CachingProviders for the {@link ClassLoader}
        // this may automatically load the CachingProvider we desire
        getCachingProviders(serviceClassLoader);
        providers = cachingProviders.get(serviceClassLoader);
      }

      CachingProvider provider = providers.get(fullyQualifiedClassName);

      if (provider == null) {
        provider = loadCachingProvider(fullyQualifiedClassName, serviceClassLoader);
        providers.put(fullyQualifiedClassName, provider);
      }

      return provider;
    }
  }
}

Caching Annotations

注解为用户提供的与缓存交互的类提供了方法拦截器。相关类在 javax.cache.annotation 中定义了下面几种:

@CacheDefaults
@CacheResult
@CachePut
@CacheRemove
@CacheRemoveAll

注解是缓存操作API的一次封装。 无论是通过注解还是通过使用预定义的Java API操作,都必须获得相同的结果。

注释是与缓存交互的新方式。仅最常用的缓存方法有对应的注解。

为了在应用程序中使用注解,需要一个库或框架来处理这些注释并拦截对带注释的应用程序对象的调用。 在一个应用程序中,处理类缓存注释的方法和配置留给实现来决定。

这通常将由 CDI 定义的依赖注入框架提供。 RI 包括与 CDI,Spring 和 Guice 一起使用的示例实现。

Annotations

@CacheDefaults

这是一个类级别的注释,用于为类中使用的所有与缓存相关的注释定义默认属性值。 可以指定 cacheNamecacheResolverFactorycacheKeyGenerator 属性,所有属性都是可选的。

如果在类上指定了 @CacheDefaults ,但是不存在方法级的缓存注释,则将忽略 @CacheDefaults 注释。

下面的示例将一个名为 “cities” 的缓存。 getCity 方法上的 @CacheResult 注解将在运行时使用此缓存名称。

@CacheDefaults(cacheName=”cities”)
public class CitySource {
    @CacheResult
    public City getCity(int lat, int lon) {
        //...
    }
}

注解定义如下:

package javax.cache.annotation;

import javax.cache.CacheManager;
import javax.enterprise.util.Nonbinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;


/**
 * Allows the configuration of defaults for {@link CacheResult}, {@link CachePut},
 * {@link CacheRemove}, and {@link CacheRemoveAll} at the class level. Without
 * the method level annotations this annotation has no effect.
 * <p>
 * Following is an example of specifying a default cache name that is used by
 * the annotations on the getDomain and deleteDomain methods. The annotation for
 * getAllDomains would use the "allDomains" cache name specified in the method
 * level annotation.
 * <pre><code>
 * package my.app;
 *
 *  CacheDefaults(cacheName="domainCache")
 * public class DomainDao {
 *    CacheResult
 *   public Domain getDomain(String domainId, int index) {
 *     ...
 *   }
 *
 *    CacheRemove
 *   public void deleteDomain(String domainId, int index) {
 *     ...
 *   }
 *
 *    CacheResult(cacheName="allDomains")
 *   public List<Domain> getAllDomains() {
 *     ...
 *   }
 * }
 * </code></pre>
 *
 * @author Rick Hightower
 * @since 1.0
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheDefaults {

  /**
   * The default name of the cache for the annotated class
   * <p>
   * If not specified defaults to:
   * package.name.ClassName.methodName(package.ParameterType,package.ParameterType)
   * <p>
   * Applicable for {@link CacheResult}, {@link CachePut}, {@link CacheRemove},
   * and {@link CacheRemoveAll}
   */
  @Nonbinding String cacheName() default "";

  /**
   * The {@link CacheResolverFactory} used to find the {@link CacheResolver} to
   * use at runtime.
   * <p>
   * The default resolver pair will resolve the cache by name from the default
   * {@link CacheManager}
   * <p>
   * Applicable for {@link CacheResult}, {@link CachePut}, {@link CacheRemove},
   * and {@link CacheRemoveAll}
   */
  @Nonbinding Class<? extends CacheResolverFactory> cacheResolverFactory()
      default CacheResolverFactory.class;

  /**
   * The {@link CacheKeyGenerator} to use to generate the
   * {@link GeneratedCacheKey} for interacting with the specified Cache.
   * <p>
   * Defaults to a key generator that uses {@link Arrays#deepHashCode(Object[])}
   * and {@link Arrays#deepEquals(Object[], Object[])} with the array returned by
   * {@link CacheKeyInvocationContext#getKeyParameters()}
   * </p>
   * Applicable for {@link CacheResult}, {@link CachePut}, and {@link CacheRemove}
   *
   * @see CacheKey
   */
  @Nonbinding Class<? extends CacheKeyGenerator> cacheKeyGenerator()
      default CacheKeyGenerator.class;
}

@CacheResult

这是方法级别的注解,用于标记使用从方法参数生成的键来缓存其返回值的方法,并在以后的调用中使用相同参数从缓存中返回这些键。

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.enterprise.util.Nonbinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * When a method annotated with {@link CacheResult} is invoked a
 * {@link GeneratedCacheKey} will be generated and {@link Cache#get(Object)} is
 * called before the annotated method actually executes. If a value is found in the
 * cache it is returned and the annotated method is never actually executed. If no
 * value is found the annotated method is invoked and the returned value is stored
 * in the cache with the generated key.
 * <p>
 * Exceptions are not cached by default. Caching of exceptions can be enabled by
 * specifying an {@link #exceptionCacheName()}. If an exception cache is specified
 * it is checked before invoking the annotated method and if a cached exception is
 * found it is re-thrown.
 * <p>
 * The {@link #cachedExceptions()} and {@link #nonCachedExceptions()} properties
 * can be used to control the exceptions are cached and those that are not.
 * <p>
 * To always invoke the annotated method and still cache the result set
 * {@link #skipGet()} to true. This will disable the pre-invocation
 * {@link Cache#get(Object)} call. If {@link #exceptionCacheName()} is
 * specified the pre-invocation exception check is also disabled. This feature is
 * useful for methods that create or update objects to be cached.
 * <p>
 * Example of caching the Domain object with a key generated from the
 * <code>String</code> and <code>int</code> parameters.
 * <p>
 * With no {@link #cacheName()} specified a cache name of
 * "my.app.DomainDao.getDomain(java.lang.String,int)" will be generated.
 * </p>
 * <pre><code>
 * package my.app;
 *
 * public class DomainDao {
 *    CacheResult
 *   public Domain getDomain(String domainId, int index) {
 *     ...
 *   }
 * }
 * </code></pre>
 * <p>
 * Example using the {@link GeneratedCacheKey} annotation so that only the domainId
 * parameter is used in key generation:
 * <pre><code>
 * package my.app;
 *
 * public class DomainDao {
 *    CacheResult
 *   public Domain getDomain(@CacheKey String domainId, Monitor mon) {
 *     ...
 *   }
 * }
 * </code></pre>
 * <p>
 * If exception caching is enabled via specification of
 * {@link #exceptionCacheName()} the following rules are used to determine if a
 * thrown exception is cached:
 * <ol>
 * <li>If {@link #cachedExceptions()} and {@link #nonCachedExceptions()} are both
 * empty then all exceptions are cached</li>
 * <li>If {@link #cachedExceptions()} is specified and
 * {@link #nonCachedExceptions()} is not specified then only exceptions
 * that pass an instanceof check against the cachedExceptions list are cached</li>
 * <li>If {@link #nonCachedExceptions()} is specified and
 * {@link #cachedExceptions()} is not specified then all exceptions
 * that do not pass an instanceof check against the nonCachedExceptions list are
 * cached</li>
 * <li>If {@link #cachedExceptions()} and {@link #nonCachedExceptions()} are both
 * specified then exceptions that pass an instanceof check against the
 * cachedExceptions list but do not pass an instanceof check against the
 * nonCachedExceptions list are cached</li>
 * </ol>
 *
 * @author Eric Dalquist
 * @author Rick Hightower
 * @see CacheKey
 * @since 1.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheResult {

  /**
   * <p>
   * The name of the cache.
   * </p>
   * If not specified defaults first to {@link CacheDefaults#cacheName()} and if
   * that is not set it defaults to:
   * package.name.ClassName.methodName(package.ParameterType,package.ParameterType)
   */
  @Nonbinding String cacheName() default "";

  /**
   * If set to true the pre-invocation {@link Cache#get(Object)} is
   * skipped and the annotated method is always executed with the returned value
   * being cached as normal. This is useful for create or update methods that
   * should always be executed and have their returned value placed in the cache.
   * <p>
   * If true and an {@link #exceptionCacheName()} is specified the pre-invocation
   * check for a thrown exception is also skipped. If an exception is thrown during
   * invocation it will be cached following the standard exception caching rules.
   * <p>
   * Defaults to false.
   *
   * @see CachePut
   */
  @Nonbinding boolean skipGet() default false;

  /**
   * The {@link CacheResolverFactory} used to find the {@link CacheResolver} to
   * use at runtime.
   * <p>
   * The default resolver pair will resolve the cache by name from the default
   * {@link CacheManager}
   */
  @Nonbinding Class<? extends CacheResolverFactory> cacheResolverFactory()
      default CacheResolverFactory.class;

  /**
   * The {@link CacheKeyGenerator} to use to generate the {@link GeneratedCacheKey}
   * for interacting with the specified Cache.
   * <p>
   * Defaults to a key generator that uses
   * {@link java.util.Arrays#deepHashCode(Object[])} and
   * {@link java.util.Arrays#deepEquals(Object[], Object[])} with the array
   * returned by {@link CacheKeyInvocationContext#getKeyParameters()}
   *
   * @see CacheKey
   */
  @Nonbinding Class<? extends CacheKeyGenerator> cacheKeyGenerator()
      default CacheKeyGenerator.class;

  /**
   * The name of the cache to cache exceptions.
   * <p>
   * If not specified no exception caching is done.
   */
  @Nonbinding String exceptionCacheName() default "";

  /**
   * Defines zero (0) or more exception {@link Class classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that
   * <b>must</b> be cached. Only consulted if {@link #exceptionCacheName()} is
   * specified.
   */
  @Nonbinding Class<? extends Throwable>[] cachedExceptions() default {};

  /**
   * Defines zero (0) or more exception {@link Class Classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that
   * <b>must not</b> be cached. Only consulted if {@link #exceptionCacheName()}
   * is specified.
   */
  @Nonbinding Class<? extends Throwable>[] nonCachedExceptions() default {};
}

@CacheKey 注解可以用来选择参数列表的一个子集来生成缓存 key。

可选的:

  1. 可选的使用另外的缓存来缓存和重抛异常,包括仅缓存指定异常的功能。
  2. skipGet , 跳过调用 Cache.get 获取已缓存值的过程,在被注解的方法必须要执行且缓存返回值的场景中有用。

静态方法上的 @CacheResult 将会直接被忽略。

@CachePut

这是一个方法级别注释,用于标记方法,必须使用 @CacheValue 注释一个参数,将其标记为要缓存的参数。 如果未指定 @CacheValue 注解,在应用程序初始化时或方法调用时会抛出 CacheAnnotationConfigurationException

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.enterprise.util.Nonbinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;


/**
 * When a method annotated with {@link CachePut} is invoked a {@link
 * GeneratedCacheKey} will be generated and {@link Cache#put(Object,
 * Object)} will be invoked on the specified cache storing the value marked with
 * {@link CacheValue}.
 * <p>
 * The default behavior is to call {@link Cache#put(Object, Object)}
 * after the annotated method is invoked, this behavior can be changed by setting
 * {@link #afterInvocation()} to false in which case
 * {@link Cache#put(Object, Object)} will be called before the annotated method is
 * invoked.
 * <p>
 * Example of caching the Domain object with a key generated from the String and
 * int parameters. The {@link CacheValue} annotation is used to designate which
 * parameter should be stored in the "domainDao" cache.
 * <pre><code>
 * package my.app;
 *
 * public class DomainDao {
 *    CachePut(cacheName="domainCache")
 *   public void updateDomain(String domainId, int index,  CacheValue Domain
 * domain) {
 *     ...
 *   }
 * }
 * </code></pre>
 * <p>
 * Exception Handling, only used if {@link #afterInvocation()} is true.
 * <ol>
 * <li>If {@link #cacheFor()} and {@link #noCacheFor()} are both empty then all
 * exceptions prevent the put</li>
 * <li>If {@link #cacheFor()} is specified and {@link #noCacheFor()} is not
 * specified then only exceptions that pass an instanceof check against the
 * cacheFor list result in a put</li>
 * <li>If {@link #noCacheFor()} is specified and {@link #cacheFor()} is not
 * specified then all exceptions that do not pass an instanceof check against the
 * noCacheFor result in a put</li>
 * <li>If {@link #cacheFor()} and {@link #noCacheFor()} are both specified then
 * exceptions that pass an instanceof check against the cacheFor list but do not
 * pass an instanceof check against the noCacheFor list result in a put</li>
 * </ol>
 *
 * @author Eric Dalquist
 * @author Rick Hightower
 * @see CacheValue
 * @see CacheKey
 * @since 1.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CachePut {

  /**
   * The name of the cache.
   * <p>
   * If not specified defaults first to {@link CacheDefaults#cacheName()} and if
   * that is not set it defaults to:
   * package.name.ClassName.methodName(package.ParameterType,package.ParameterType)
   */
  @Nonbinding String cacheName() default "";

  /**
   * When {@link Cache#put(Object, Object)} should be called. If true it is called
   * after the annotated method invocation completes successfully. If false it is
   * called before the annotated method is invoked.
   * <p>
   * Defaults to true.
   * <p>
   * If true and the annotated method throws an exception the rules governing
   * {@link #cacheFor()} and {@link #noCacheFor()} will be followed.
   */
  @Nonbinding boolean afterInvocation() default true;

  /**
   * The {@link CacheResolverFactory} used to find the {@link CacheResolver} to
   * use at runtime.
   * <p>
   * The default resolver pair will resolve the cache by name from the default
   * {@link CacheManager}
   */
  @Nonbinding Class<? extends CacheResolverFactory> cacheResolverFactory()
      default CacheResolverFactory.class;

  /**
   * The {@link CacheKeyGenerator} to use to generate the {@link
   * GeneratedCacheKey} for interacting with the specified Cache.
   * <p>
   * Defaults to a key generator that uses {@link Arrays#deepHashCode(Object[])}
   * and {@link Arrays#deepEquals(Object[], Object[])} with the array
   * returned by {@link CacheKeyInvocationContext#getKeyParameters()}
   *
   * @see CacheKey
   */
  @Nonbinding Class<? extends CacheKeyGenerator> cacheKeyGenerator()
      default CacheKeyGenerator.class;

  /**
   * Defines zero (0) or more exception {@link Class classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that <b>must</b>
   * cause the parameter to be cached. Only used if {@link #afterInvocation()} is
   * true.
   */
  @Nonbinding Class<? extends Throwable>[] cacheFor() default {};

  /**
   * Defines zero (0) or more exception {@link Class Classes}, which must be a
   * subclass of {@link Throwable}, indicating which exception types <b>must
   * not</b> cause the parameter to be cached. Only used if
   * {@link #afterInvocation()} is true.
   */
  @Nonbinding Class<? extends Throwable>[] noCacheFor() default {};
}

@CacheKey 注解可以用来选择参数列表的一个子集来生成缓存 key。 @CacheValue 注解的参数不会参与 key 的生成。

Options

Cache.put
Cache.put

静态方法的 @CachePut 将直接忽略。

@CacheRemove

这是一个方法级注解,用于标记从指定 Cache 中删除条目的方法。

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.enterprise.util.Nonbinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * When a method annotated with {@link CacheRemove} is invoked a {@link
 * GeneratedCacheKey} will be generated and {@link Cache#remove(Object)} will be
 * invoked on the specified cache.
 * <p>
 * The default behavior is to call {@link Cache#remove(Object)} after
 * the annotated method is invoked, this behavior can be changed by setting
 * {@link #afterInvocation()} to false in which case
 * {@link Cache#remove(Object)} will be called before the annotated
 * method is invoked.
 * <p>
 * Example of removing a specific Domain object from the "domainCache". A {@link
 * GeneratedCacheKey} will be generated from the String and int parameters and
 * used to call {@link Cache#remove(Object)} after the deleteDomain
 * method completes successfully.
 * <pre><code>
 * package my.app;
 *
 * public class DomainDao {
 *    CacheRemove(cacheName="domainCache")
 *   public void deleteDomain(String domainId, int index) {
 *     ...
 *   }
 * }
 * </code></pre>
 * <p>
 * Exception Handling, only used if {@link #afterInvocation()} is true.
 * <ol>
 * <li>If {@link #evictFor()} and {@link #noEvictFor()} are both empty then all
 * exceptions prevent the remove</li>
 * <li>If {@link #evictFor()} is specified and {@link #noEvictFor()} is not
 * specified then only exceptions that pass an instanceof check against the
 * evictFor list result in a remove</li>
 * <li>If {@link #noEvictFor()} is specified and {@link #evictFor()} is not
 * specified then all exceptions that do not pass an instanceof check against the
 * noEvictFor result in a remove</li>
 * <li>If {@link #evictFor()} and {@link #noEvictFor()} are both specified then
 * exceptions that pass an instanceof check against the evictFor list but do not
 * pass an instanceof check against the noEvictFor list result in a remove</li>
 * </ol>
 *
 * @author Eric Dalquist
 * @author Rick Hightower
 * @author Greg Luck
 * @see CacheKey
 * @since 1.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRemove {

  /**
   * The name of the cache.
   * <p>
   * If not specified defaults first to {@link CacheDefaults#cacheName()},
   * and if that is not set then to:
   * package.name.ClassName.methodName(package.ParameterType,package.ParameterType)
   */
  @Nonbinding String cacheName() default "";

  /**
   * When {@link Cache#remove(Object)}  should be called. If true it is called
   * after the annotated method invocation completes successfully. If false it is
   * called before the annotated method is invoked.
   * <p>
   * Defaults to true.
   * <p>
   * If true and the annotated method throws an exception the remove will not be
   * executed.
   */
  @Nonbinding boolean afterInvocation() default true;

  /**
   * The {@link CacheResolverFactory} used to find the {@link CacheResolver} to
   * use at runtime.
   * <p>
   * The default resolver pair will resolve the cache by name from the default
   * {@link CacheManager}
   */
  @Nonbinding Class<? extends CacheResolverFactory> cacheResolverFactory()
      default CacheResolverFactory.class;

  /**
   * The {@link CacheKeyGenerator} to use to generate the {@link
   * GeneratedCacheKey} for interacting with the specified Cache.
   * <p>
   * Defaults to a key generator that uses
   * {@link java.util.Arrays#deepHashCode(Object[])}
   * and {@link java.util.Arrays#deepEquals(Object[], Object[])} with the array
   * returned by {@link CacheKeyInvocationContext#getKeyParameters()}
   *
   * @see CacheKey
   */
  @Nonbinding Class<? extends CacheKeyGenerator> cacheKeyGenerator()
      default CacheKeyGenerator.class;

  /**
   * Defines zero (0) or more exception {@link Class classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that must cause
   * a cache eviction. Only used if {@link #afterInvocation()} is true.
   */
  @Nonbinding Class<? extends Throwable>[] evictFor() default {};

  /**
   * Defines zero (0) or more exception {@link Class Classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that must
   * <b>not</b> cause a cache eviction. Only used if {@link #afterInvocation()} is
   * true.
   */
  @Nonbinding Class<? extends Throwable>[] noEvictFor() default {};
}

@CacheKey 注解可以用来选择参数列表的一个子集来生成缓存 key。

Options

Cache.remove
Cache.remove

静态方法的 @CacheRemove 将直接忽略。

@CacheRemoveAll

这是一个方法级的注解,用于标记执行完成后删除所有条目的方法。

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.enterprise.util.Nonbinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * When a method annotated with {@link CacheRemoveAll} is invoked all elements in
 * the specified cache will be removed via the
 * {@link Cache#removeAll()} method
 * <p>
 * The default behavior is to call {@link Cache#removeAll()} after the
 * annotated method is invoked, this behavior can be changed by setting {@link
 * #afterInvocation()} to false in which case {@link Cache#removeAll()}
 * will be called before the annotated method is invoked.
 * <p>
 * Example of removing all Domain objects from the "domainCache". {@link
 * Cache#removeAll()} will be called after deleteAllDomains() returns
 * successfully.
 * <pre><code>
 * package my.app;
 *
 * public class DomainDao {
 *    CacheRemoveAll(cacheName="domainCache")
 *   public void deleteAllDomains() {
 *     ...
 *   }
 * }
 * </code></pre>
 * <p>
 * Exception Handling, only used if {@link #afterInvocation()} is true.
 * <ol>
 * <li>If {@link #evictFor()} and {@link #noEvictFor()} are both empty then all
 * exceptions prevent the removeAll</li>
 * <li>If {@link #evictFor()} is specified and {@link #noEvictFor()} is not
 * specified then only exceptions that pass an instanceof check against the
 * evictFor list result in a removeAll</li>
 * <li>If {@link #noEvictFor()} is specified and {@link #evictFor()} is not
 * specified then all exceptions that do not pass an instanceof check against the
 * noEvictFor result in a removeAll</li>
 * <li>If {@link #evictFor()} and {@link #noEvictFor()} are both specified then
 * exceptions that pass an instanceof check against the evictFor list but do not
 * pass an instanceof check against the noEvictFor list result in a removeAll</li>
 * </ol>
 *
 * @author Eric Dalquist
 * @author Rick Hightower
 * @since 1.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRemoveAll {

  /**
   * /**
   * The name of the cache.
   * <p>
   * If not specified defaults first to {@link CacheDefaults#cacheName()} and if
   * that is not set it defaults to:
   * package.name.ClassName.methodName(package.ParameterType,package.ParameterType)
   */
  @Nonbinding String cacheName() default "";

  /**
   * When {@link Cache#removeAll()} should be called. If true it is called after
   * the annotated method invocation completes successfully. If false it is called
   * before the annotated method is invoked.
   * <p>
   * Defaults to true.
   * <p>
   * If true and the annotated method throws an exception the removeAll will not be
   * executed.
   */
  @Nonbinding boolean afterInvocation() default true;

  /**
   * The {@link CacheResolverFactory} used to find the {@link CacheResolver} to
   * use at runtime.
   * <p>
   * The default resolver pair will resolve the cache by name from the default
   * {@link CacheManager}
   */
  @Nonbinding Class<? extends CacheResolverFactory> cacheResolverFactory()
      default CacheResolverFactory.class;

  /**
   * Defines zero (0) or more exception {@link Class classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that must
   * cause a cache eviction. Only used if {@link #afterInvocation()} is true.
   */
  @Nonbinding Class<? extends Throwable>[] evictFor() default {};

  /**
   * Defines zero (0) or more exception {@link Class Classes}, that must be a
   * subclass of {@link Throwable}, indicating the exception types that must
   * <b>not</b> cause a cache eviction. Only used if {@link #afterInvocation()} is
   * true.
   */
  @Nonbinding Class<? extends Throwable>[] noEvictFor() default {};
}

Options

Cache.removeAll
Cache.removeAll

静态方法的 @CacheRemove 将直接忽略。

@CacheKey

这是一个参数级的注解来标记指定参数需要用于 key 的生成。在运行时,带有 @CacheKey 的参数可以用 CacheKeyInvocationContext.getKeyParameters() 来获取

一般与下列注解一起使用。

@CacheResult
@CachePut
@CacheRemove

@CacheValue

这是一个参数级别的注解,用户标记使用 @CachePut 注解的方法要缓存的参数。被注解的参数不会在 CacheKeyInvocationContext.getKeyParameters() 中。只与 @CachePut 一起使用。

一个例子

/**
 * An implementation of BlogManager that uses a variety of annotations
 * @author Rick Hightower
 */
@CacheDefaults(cacheName = "blgMngr")
public class ClassLevelCacheConfigBlogManagerImpl implements BlogManager {

  private static Map<String, Blog> map = new HashMap<String, Blog>();

  @CacheResult
  public Blog getEntryCached(String title) {
    return map.get(title);
  }

  public Blog getEntryRaw(String title) {
    return map.get(title);
  }

  /**
   * @see manager.BlogManager#clearEntryFromCache(java.lang.String)
   */
  @CacheRemove
  public void clearEntryFromCache(String title) {
  }

  public void clearEntry(String title) {
    map.put(title, null);
  }

  @CacheRemoveAll
  public void clearCache() {
  }

  public void createEntry(Blog blog) {
    map.put(blog.getTitle(), blog);
  }

  @CacheResult
  public Blog getEntryCached(String randomArg, @CacheKey String title,
                             String randomArg2) {
    return map.get(title);
  }
}

缓存解析

所有方法级别的注释都允许指定 CacheResolverFactory 和缓存名称,用于确定在运行时要与之交互的缓存。

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import java.lang.annotation.Annotation;

/**
 * Determines the {@link CacheResolver} to use for an annotated method. The
 * {@link CacheResolver} will be retrieved once per annotated method.
 * <p>
 * Implementations MUST be thread-safe.
 *
 * @author Eric Dalquist
 * @since 1.0
 */
public interface CacheResolverFactory {

  /**
   * Get the {@link CacheResolver} used at runtime for resolution of the
   * {@link Cache} for the {@link CacheResult}, {@link CachePut},
   * {@link CacheRemove}, or {@link CacheRemoveAll} annotation.
   *
   * @param cacheMethodDetails The details of the annotated method to get the
   *                           {@link CacheResolver} for. @return The {@link
   *                           CacheResolver} instance to be
   *                           used by the interceptor.
   */
  CacheResolver getCacheResolver(CacheMethodDetails<? extends Annotation>
                                     cacheMethodDetails);

  /**
   * Get the {@link CacheResolver} used at runtime for resolution of the {@link
   * Cache} for the {@link CacheResult} annotation to cache exceptions.
   * <p>
   * Will only be called if {@link CacheResult#exceptionCacheName()} is not empty.
   *
   * @param cacheMethodDetails The details of the annotated method to get the
   *                           {@link CacheResolver} for.
   * @return The {@link CacheResolver} instance to be used by the interceptor.
   */
  CacheResolver getExceptionCacheResolver(CacheMethodDetails<CacheResult>
                                              cacheMethodDetails);
}

缓存名

如果方法级注解和类级的注解都没有定义缓存名的话,将使用如下方式生成默认的缓存名:

package.name.ClassName.methodName(package.ParameterType,package.ParameterType)

@CacheResult 注解有 exceptionCacheName 属性,如果未指定此属性,则没有默认的异常缓存名,并且不使用异常缓存。

缓存解析器工厂

必须为每个带注释的方法必须精确地调用一次指定的 CacheResolverFactory 来确定每次方法执行都要使用的 CacheResolver 。 当执行带注释的方法时,先前获取的 CacheResolver 加上 CacheInvocationContext 确定要使用的缓存。

如果 javax.cache.annotation.CacheResolverFactory 在方法注解和 @CacheDefaults 上都指定了,那么会使用默认的 CacheResolverFactory

默认的 CacheResolverFactory 按顺序做以下的操作:

  • 获取 CacheManager
CachingProvider provider = Caching.getCachingProvider();
CacheManager cacheManager =  provider.getCacheManager(provider.getDefaultURI(), provider.getDefaultClassLoader());
CacheManager.getCache(String cacheName)
Cache
Cache cache = cacheManager.createCache(cacheName, new MutableConfiguration());
  • 使用获取到的 Cache 创建一个 CacheResolver

如果 CacheResolverFactory 执行时抛了一个异常,异常需要向上抛到触发 CacheResolverFactory 执行的位置

CacheResolver

CacheResolverCacheResolverFactory 返回,并且在每个被注解的方法被调用时返回对应的 Cache

定义如下:

package javax.cache.annotation;

import javax.cache.Cache;
import java.lang.annotation.Annotation;

/**
 * Determines the {@link Cache} to use for an intercepted method invocation.
 * <p>
 * Implementations MUST be thread-safe.
 *
 * @author Eric Dalquist
 * @see CacheResolverFactory
 * @since 1.0
 */
public interface CacheResolver {

  /**
   * Resolve the {@link Cache} to use for the {@link CacheInvocationContext}.
   *
   * @param <K> the type of key
   * @param <V> the type of value
   * @param cacheInvocationContext The context data for the intercepted method
   *                               invocation
   * @return The {@link Cache} instance to be used by the interceptor
   */
  <K, V> Cache<K, V> resolveCache(CacheInvocationContext<? extends Annotation>
                                      cacheInvocationContext);
}

如果 CacheResolver 抛了异常,那么异常需要需要向上传播到 触发 CacheResolverFactory 执行的位置。

key 生成

@CacheResult@CachePut@CacheRemove 注解均需要生成缓存键,并且这些注解均允许指定 CacheKeyGenerator 的实现。

指定的 CacheKeyGenerator 将为每个带注释的方法调用被调用一次。 CacheKeyInvocationContext 提供有关带注释的方法和当前调用的信息。由开发人员指定要在键中使用的方法参数包含在 getKeyParameters() 方法返回的 CacheInvocationParameter 数组中。定制的 CacheKeyGenerator 可以使用任何可用的信息来生成 GeneratedCacheKey

如果未在注解和 @CacheDefaults 上指定 javax.cache.annotation.CacheKeyGenerator ,则必须使用默认的 CacheKeyGenerator

默认 CacheKeyGenerator 生成方法:

  • CacheKeyInvocationContext.getKeyParameters() 中拿到 CacheInvocationParameter ,然后调用 getValue() 创建 Object []
  • 创建一个 CacheKey 实例,该实例包装 Object [] 并使用 Arrays.deepHashCode 计算其 hashCodeArrays.deepEquals 以与其他键进行比较。

如果 CacheKeyGenerator 产生异常,则必须将该异常传播到触发 CacheKeyGenerator 执行的应用程序代码。

注解支持类

CacheMethodDetails

提供被缓存注解标记的方法的静态新。 CacheResolverFactory 用这个来确定运行时使用的 CacheResolver

接口定义如下:

package javax.cache.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * Static information about a method annotated with one of:
 * {@link CacheResult}, {@link CachePut}, {@link CacheRemove}, or {@link
 * CacheRemoveAll}
 * <p>
 * Used with {@link CacheResolverFactory#getCacheResolver(CacheMethodDetails)} to
 * determine the {@link CacheResolver} to use with the method.
 *
 * @param <A> The type of annotation this context information is for. One of
 *            {@link CacheResult}, {@link CachePut}, {@link CacheRemove}, or
 *            {@link CacheRemoveAll}.
 * @author Eric Dalquist
 * @see CacheResolverFactory
 */
public interface CacheMethodDetails<A extends Annotation> {
  /**
   * The annotated method
   *
   * @return The annotated method
   */
  Method getMethod();

  /**
   * An immutable Set of all Annotations on this method
   *
   * @return An immutable Set of all Annotations on this method
   */
  Set<Annotation> getAnnotations();

  /**
   * The caching related annotation on the method.
   * One of: {@link CacheResult}, {@link CachePut}, {@link CacheRemove}, or
   * {@link CacheRemoveAll}
   *
   * @return The caching related annotation on the method.
   */
  A getCacheAnnotation();

  /**
   * The cache name resolved by the implementation.
   * <p>
   * The cache name is determined by first looking at the cacheName attribute of
   * the method level annotation. If that attribute is not set then the class
   * level {@link CacheDefaults} annotation is checked. If that annotation does
   * not exist or does not have its cacheName attribute set then the following
   * cache name generation rules are followed:
   * <p>
   * "fully qualified class name"."method name"("fully qualified parameter class
   * names")
   * <p>
   * For example:
   * <pre><code>
   * package my.app;
   *
   * public class DomainDao {
   *    CacheResult
   *   public Domain getDomain(String domainId, int index) {
   *     ...
   *   }
   * }
   * </code></pre>
   * <p>
   * Results in the cache name: "my.app.DomainDao.getDomain(java.lang.String,int)"
   *
   * @return The fully resolved cache name
   */
  String getCacheName();
}

CacheInvocationContext

包含了被缓存注解标记的方法执行时的运行时信息。 CacheResolver 用这个来确定需要使用哪个 Cache 。拓展自 CacheMethodDetails ,所以静态信息对运行时可用。

接口定义如下:

package javax.cache.annotation;

import java.lang.annotation.Annotation;

/**
 * Runtime information about an intercepted method invocation for a method
 * annotated with {@link CacheResult}, {@link CachePut}, {@link CacheRemove},
 * or {@link CacheRemoveAll}
 * <p>
 * Used with {@link CacheResolver#resolveCache(CacheInvocationContext)} to
 * determine the {@link javax.cache.Cache} to use at runtime for the method
 * invocation.
 *
 * @param <A> The type of annotation this context information is for. One of
 *            {@link CacheResult}, {@link CachePut}, {@link CacheRemove}, or {@link
 *            CacheRemoveAll}.
 * @author Eric Dalquist
 * @see CacheResolver
 */
public interface CacheInvocationContext<A extends Annotation>
    extends CacheMethodDetails<A> {

  /**
   * @return The object the intercepted method was invoked on.
   */
  Object getTarget();

  /**
   * Returns a clone of the array of all method parameters.
   *
   * @return An array of all parameters for the annotated method
   */
  CacheInvocationParameter[] getAllParameters();

  /**
   * Return an object of the specified type to allow access to the
   * provider-specific API. If the provider's
   * implementation does not support the specified class, the {@link
   * IllegalArgumentException} is thrown.
   *
   * @param <T> The type of the expected underlying {@link javax.cache.Cache} implementation.
   * @param cls the class of the object to be returned. This is normally either the
   *            underlying implementation class or an interface that it implements.
   * @return an instance of the specified class
   * @throws IllegalArgumentException if the provider doesn't support the specified
   *            class.
   */
  <T> T unwrap(java.lang.Class<T> cls);
}

CacheKeyInvocationContext

用于生成 key 时所需的运行时信息。 CacheKeyGenerator 用这个来生成缓存key,接口拓展自 CacheInvocationContext

接口定义如下:

package javax.cache.annotation;

import java.lang.annotation.Annotation;

/**
 * Runtime information about an intercepted method invocation for a method
 * annotated with {@link CacheResult}, {@link CachePut}, or
 * {@link CacheRemove}.
 * <p>
 * Used with {@link CacheKeyGenerator#generateCacheKey(CacheKeyInvocationContext)}
 * to generate a {@link GeneratedCacheKey} for the invocation.
 *
 * @param <A> The type of annotation this context information is for. One of
 *            {@link CacheResult}, {@link CachePut}, or {@link CacheRemove}.
 * @author Eric Dalquist
 * @see CacheKeyGenerator
 */
public interface CacheKeyInvocationContext<A extends Annotation>
    extends CacheInvocationContext<A> {

  /**
   * Returns a clone of the array of all method parameters to be used by the
   * {@link
   * CacheKeyGenerator} in creating a {@link GeneratedCacheKey}. The returned array
   * may be the same as or a subset of the array returned by
   * {@link #getAllParameters()}
   * <p>
   * Parameters in this array are selected by the following rules:
   * <ul>
   * <li>If no parameters are annotated with {@link CacheKey} or {@link
   * CacheValue}
   * then all parameters are included</li>
   * <li>If a {@link CacheValue} annotation exists and no {@link CacheKey} then
   * all
   * parameters except the one annotated with {@link CacheValue} are included</li>
   * <li>If one or more {@link CacheKey} annotations exist only those parameters
   * with the {@link CacheKey} annotation are included</li>
   * </ul>
   *
   * @return An array of all parameters to be used in cache key generation
   */
  CacheInvocationParameter[] getKeyParameters();

  /**
   * When a method is annotated with {@link CachePut} this is the parameter
   * annotated with {@link CacheValue}
   *
   * @return The parameter to cache, will never be null for methods annotated with
   *         {@link CachePut}, will be null for methods not annotated with {@link
   *         CachePut}
   */
  CacheInvocationParameter getValueParameter();
}

CacheInvocationParameter

方法执行时,其中一个参数的运行时信息。包括参数注解,位置,类型和值,由 CacheInvocationContextCacheKeyInvocationContext 来提供。

接口定义如下:

package javax.cache.annotation;

import java.lang.annotation.Annotation;
import java.util.Set;

/**
 * A parameter to an intercepted method invocation. Contains the parameter value
 * as well static type and annotation information about the parameter.
 *
 * @author Eric Dalquist
 */
public interface CacheInvocationParameter {

  /**
   * The parameter type as declared on the method.
   */
  Class<?> getRawType();

  /**
   * @return The parameter value
   */
  Object getValue();

  /**
   * @return An immutable Set of all Annotations on this method parameter, never
   * null.
   */
  Set<Annotation> getAnnotations();

  /**
   * The index of the parameter in the original parameter array as returned by
   * {@link CacheInvocationContext#getAllParameters()}
   *
   * @return The index of the parameter in the original parameter array.
   */
  int getParameterPosition();
}

GeneratedCacheKey

CacheKeyGenerator 接口创建,这个用于表示与 Cache 交互的 key,所有的 GeneratedCacheKey 都不可变且可序列化。

定义如下:

package javax.cache.annotation;

import java.io.Serializable;

/**
 * A {@link Serializable}, immutable, thread-safe object that is used as a key,
 * automatically generated by a {@link CacheKeyGenerator}.
 * <p>
 * The implementation MUST follow the Java contract for {@link Object#hashCode()}
 * and {@link Object#equals(Object)} to ensure correct behavior.
 * <p>
 * It is recommended that implementations also override {@link Object#toString()}
 * and provide a human-readable string representation of the key.
 *
 * @author Eric Dalquist
 * @see CacheKeyGenerator
 * @since 1.0
 */
public interface GeneratedCacheKey extends Serializable {

  /**
   * The immutable hash code of the cache key.
   *
   * @return The hash code of the object
   * @see Object#hashCode()
   */
  @Override
  int hashCode();

  /**
   * Compare this {@link GeneratedCacheKey} with another. If the two objects
   * are equal their {@link #hashCode()} values MUST be equal as well.
   *
   * @param object The other object to compare to.
   * @return true if the objects are equal
   * @see Object#equals(Object)
   */
  @Override
  boolean equals(Object object);
}

注解交互

注解继承与排序

本规范遵循Java通用规范[2]的第2.1节中关于注释继承的规定。 关于本规范之外的注释,拦截器的执行顺序未定义,而是留给注解支持方实现。

多注解

在一个方法上只能指定一个方法级别的缓存注解,而在一个参数上只能指定一个参数的级别缓存注解。 如果在一个方法上指定了多个注解,则建议注解处理器抛出特定于实现的异常或记录警告。

管理

javax.cache.management 包里包含了用于缓存管理和统计的 MXBean 接口。

启用和禁用

缓存管理和统计默认是关闭的,为了使用它们需要使用 MutableConfiguration

setManagementEnabled(boolean enabled) // 管理是否启用
setStatisticsEnabled(boolean enabled) // 统计是否启用

为了在运行时启用禁用, CacheManager 提供了一下两个方法:

/**
 * Controls whether management is enabled. If enabled the
 * {@link javax.cache.management.CacheMXBean} for each cache is registered in
 * the platform MBean server. THe platform MBeanServer is obtained using
 * {@link java.lang.management.ManagementFactory#getPlatformMBeanServer()}
 * <p/>
 * Management information includes the name and configuration information for
 * the cache.
 * <p/>
 * Each cache's management object must be registered with an ObjectName that
 * is unique and has the following type and attributes:
 * <p/>
 * Type:
 * <code>javax.cache:type=Cache</code>
 * <p/>
 * Required Attributes:
 * <ul>
 * <li>CacheManager the name of the CacheManager
 * <li>Cache the name of the Cache
 * </ul>
 *
 * @param cacheName the name of the cache to register
 * @param enabled   true to enable management, false to disable.
 */
void enableManagement(String cacheName, boolean enabled);
/**
 * Enables or disables statistics gathering for a managed {@link Cache} at
 * runtime.
 * <p/>
 * Each cache's statistics object must be registered with an ObjectName that
 * is unique and has the following type and attributes:
 * <p/>
 * Type:
 * <code>javax.cache:type=CacheStatistics</code>
 * <p/>
 * Required Attributes:
 * <ul>
 * <li>CacheManager the name of the CacheManager
 * <li>Cache the name of the Cache
 * </ul>
 *
 * @param cacheName the name of the cache to register
 * @param enabled   true to enable statistics, false to disable.
 * @throws IllegalStateException if the cache is {@link #isClosed()}
 * @throws NullPointerException  if cacheName is null
 */
void enableStatistics(String cacheName, boolean enabled);

MXBean 定义

CacheMXBean 提供了缓存配置的详细信息,接口定义如下:

package javax.cache.management;

import javax.cache.Cache;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheWriter;
import javax.management.MXBean;

/**
 * A management bean for cache. It provides configuration information. It does not
 * allow mutation of configuration or mutation of the cache.
 * <p>
 * Each cache's management object must be registered with an ObjectName that is
 * unique and has the following type and attributes:
 * <p>
 * Type:
 * <code>javax.cache:type=CacheConfiguration</code>
 * <p>
 * Required Attributes:
 * <ul>
 * <li>CacheManager the URI of the CacheManager
 * <li>Cache the name of the Cache
 * </ul>
 *
 * @author Greg Luck
 * @author Yannis Cosmadopoulos
 * @since 1.0
 */
@MXBean
public interface CacheMXBean {

  /**
   * Determines the required type of keys for this {@link Cache}, if any.
   *
   * @return the fully qualified class name of the key type,
   * or "java.lang.Object" if the type is undefined.
   */
  String getKeyType();

  /**
   * Determines the required type of values for this {@link Cache}, if any.
   * @return the fully qualified class name of the value type,
   * or "java.lang.Object" if the type is undefined.
   */
  String getValueType();

  /**
   * Determines if a {@link Cache} should operate in read-through mode.
   * <p>
   * When in read-through mode, cache misses that occur due to cache entries
   * not existing as a result of performing a "get" call via one of
   * {@link Cache#get},
   * {@link Cache#getAll},
   * {@link Cache#getAndRemove} and/or
   * {@link Cache#getAndReplace} will appropriately
   * cause the configured {@link CacheLoader} to be
   * invoked.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return <code>true</code> when a {@link Cache} is in
   *         "read-through" mode.
   * @see CacheLoader
   */
  boolean isReadThrough();

  /**
   * Determines if a {@link Cache} should operate in "write-through"
   * mode.
   * <p>
   * When in "write-through" mode, cache updates that occur as a result of
   * performing "put" operations called via one of
   * {@link Cache#put},
   * {@link Cache#getAndRemove},
   * {@link Cache#removeAll},
   * {@link Cache#getAndPut}
   * {@link Cache#getAndRemove},
   * {@link Cache#getAndReplace},
   * {@link Cache#invoke}
   * {@link Cache#invokeAll}
   * <p>
   * will appropriately cause the configured {@link CacheWriter} to be invoked.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return <code>true</code> when a {@link Cache} is in "write-through" mode.
   * @see CacheWriter
   */
  boolean isWriteThrough();

  /**
   * Whether storeByValue (true) or storeByReference (false).
   * When true, both keys and values are stored by value.
   * <p>
   * When false, both keys and values are stored by reference.
   * Caches stored by reference are capable of mutation by any threads holding
   * the reference. The effects are:
   * <ul>
   * <li>if the key is mutated, then the key may not be retrievable or
   * removable</li>
   * <li>if the value is mutated, then all threads in the JVM can potentially
   * observe those mutations, subject to the normal Java Memory Model rules.</li>
   * </ul>
   * Storage by reference only applies to the local heap. If an entry is moved off
   * heap it will need to be transformed into a representation. Any mutations that
   * occur after transformation may not be reflected in the cache.
   * <p>
   * When a cache is storeByValue, any mutation to the key or value does not affect
   * the key of value stored in the cache.
   * <p>
   * The default value is <code>true</code>.
   *
   * @return true if the cache is store by value
   */
  boolean isStoreByValue();

  /**
   * Checks whether statistics collection is enabled in this cache.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return true if statistics collection is enabled
   */
  boolean isStatisticsEnabled();

  /**
   * Checks whether management is enabled on this cache.
   * <p>
   * The default value is <code>false</code>.
   *
   * @return true if management is enabled
   */
  boolean isManagementEnabled();

}

CacheStatisticsMXBean 提供了缓存的统计信息,定义如下:

package javax.cache.management;

import javax.management.MXBean;

/**
 * Cache statistics.
 * <p>
 * Statistics are accumulated from the time a cache is created. They can be reset
 * to zero using {@link #clear}.
 * <p>
 * There are no defined consistency semantics for statistics. Refer to the
 * implementation for precise semantics.
 * <p>
 * Each cache's statistics object must be registered with an ObjectName that is
 * unique and has the following type and attributes:
 * <p>
 * Type:
 * <code>javax.cache:type=CacheStatistics</code>
 * <p>
 * Required Attributes:
 * <ul>
 * <li>CacheManager the URI of the CacheManager
 * <li>Cache the name of the Cache
 * </ul>
 *
 * @author Greg Luck
 * @since 1.0
 */
@MXBean
public interface CacheStatisticsMXBean {

  /**
   * Clears the statistics counters to 0 for the associated Cache.
   */
  void clear();

  /**
   * The number of get requests that were satisfied by the cache.
   * <p>
   * {@link javax.cache.Cache#containsKey(Object)} is not a get request for
   * statistics purposes.
   * <p>
   * In a caches with multiple tiered storage, a hit may be implemented as a hit
   * to the cache or to the first tier.
   * <p>
   * For an {@link javax.cache.processor.EntryProcessor}, a hit occurs when the
   * key exists and an entry processor can be invoked against it, even if no
   * methods of {@link javax.cache.Cache.Entry} or
   * {@link javax.cache.processor.MutableEntry} are called.
   *
   * @return the number of hits
   */
  long getCacheHits();

  /**
   * This is a measure of cache efficiency.
   * <p>
   * It is calculated as:
   * {@link #getCacheHits} divided by {@link #getCacheGets ()} * 100.
   *
   * @return the percentage of successful hits, as a decimal e.g 75.
   */
  float getCacheHitPercentage();

  /**
   * A miss is a get request that is not satisfied.
   * <p>
   * In a simple cache a miss occurs when the cache does not satisfy the request.
   * <p>
   * {@link javax.cache.Cache#containsKey(Object)} is not a get request for
   * statistics purposes.
   * <p>
   * For an {@link javax.cache.processor.EntryProcessor}, a miss occurs when the
   * key does not exist and therefore an entry processor cannot be invoked
   * against it.
   * <p>
   * In a caches with multiple tiered storage, a miss may be implemented as a miss
   * to the cache or to the first tier.
   * <p>
   * In a read-through cache a miss is an absence of the key in the cache that
   * will trigger a call to a CacheLoader. So it is still a miss even though the
   * cache will load and return the value.
   * <p>
   * Refer to the implementation for precise semantics.
   *
   * @return the number of misses
   */
  long getCacheMisses();

  /**
   * Returns the percentage of cache accesses that did not find a requested entry
   * in the cache.
   * <p>
   * This is calculated as {@link #getCacheMisses()} divided by
   * {@link #getCacheGets()} * 100.
   *
   * @return the percentage of accesses that failed to find anything
   */
  float getCacheMissPercentage();

  /**
   * The total number of requests to the cache. This will be equal to the sum of
   * the hits and misses.
   * <p>
   * A "get" is an operation that returns the current or previous value. It does
   * not include checking for the existence of a key.
   * <p>
   * In a caches with multiple tiered storage, a gets may be implemented as a get
   * to the cache or to the first tier.
   *
   * @return the number of gets
   */
  long getCacheGets();

  /**
   * The total number of puts to the cache.
   * <p>
   * A put is counted even if it is immediately evicted.
   * <p>
   * Replaces, where a put occurs which overrides an existing mapping is counted
   * as a put.
   *
   * @return the number of puts
   */
  long getCachePuts();

  /**
   * The total number of removals from the cache. This does not include evictions,
   * where the cache itself initiates the removal to make space.
   *
   * @return the number of removals
   */
  long getCacheRemovals();

  /**
   * The total number of evictions from the cache. An eviction is a removal
   * initiated by the cache itself to free up space. An eviction is not treated as
   * a removal and does not appear in the removal counts.
   *
   * @return the number of evictions
   */
  long getCacheEvictions();

  /**
   * The mean time to execute gets.
   * <p>
   * In a read-through cache the time taken to load an entry on miss is not
   * included in get time.
   *
   * @return the time in µs
   */
  float getAverageGetTime();

  /**
   * The mean time to execute puts.
   *
   * @return the time in µs
   */
  float getAveragePutTime();

  /**
   * The mean time to execute removes.
   *
   * @return the time in µs
   */

  float getAverageRemoveTime();

}

访问管理信息

没有提供用于管理或统计的访问器方法。 启用后,将缓存实现将实现的 MBeanServer 注册 MXBean

然后可以按照JMX定义的常规方法从对应的 MBeanServer 获得 Bean。

简单的进程内实现可能只使用通过 ManagementFactory.getPlatformMBeanServer() 访问的 MBeanServer 平台。 缓存实现必须记录如何解析用于存储缓存管理信息的 MBeanServer

JMX 属性名称的遵循 JavaBeans [15]属性命名的约定。 因此, CacheStatisticsMXBean 上的访问器 getCacheHitPercentage() 对应于JMX属性 CacheHitPercentage

例子

这个例子展示了如何读取名称为 simpleCacheCacheCacheHitPercentage

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

MutableConfiguration<String, Integer> config =
    new MutableConfiguration<String, Integer>();
config.setTypes(String.class, Integer.class)
    .setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(ONE_HOUR))
    .setStatisticsEnabled(true);

cacheManager.createCache("simpleCache", config);
Cache<String, Integer> cache = cacheManager.getCache("simpleCache",
    String.class, Integer.class);

Set<ObjectName> registeredObjectNames = null;
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

ObjectName objectName = new ObjectName("javax.cache:type=CacheStatistics"
    + ",CacheManager=" + (cache.getCacheManager().getURI().toString())
    + ",Cache=" + cache.getName());
System.out.println(mBeanServer.getAttribute(objectName,
    "CacheHitPercentage"));

影响统计的缓存操作

下表概述了影响统计的缓存操作。在表中,如果存在,则将命中某些事件;如果不存在,则将发生未命中。该表在每一列中都将具有“是”。 如果将高速缓存设置为 Read-Through 模式,在需要调用 CacheLoader 加载数据时,也算做缓存未命中。

方法 修改 删除 命中 未命中
boolean containsKey(K key) NO NO NO NO
V get(K key) NO NO YES YES
Map<K,V> getAll(Collection<? extends K> keys) NO NO YES
V getAndPut(K key, V value) YES NO YES YES
V getAndRemove(K key) NO YES YES YES
V getAndReplace(K key, V value) YES NO YES YES
<T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)entryProcessor); setValue() 被调用时YES remove() 被调用时。 YES YES
<T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,EntryProcessor<K, V, T> entryProcessor, Object... arguments); setValue() 被调用时 remove() 被调用时。 YES YES
Iterator<Cache.Entry<K, V>> iterator() NO remove() 被调用时。 YES NO
void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) NO NO NO NO
void put(K key, V value) YES NO NO NO
void putAll(Map<? extends K,? extends V> map) YES NO NO NO
boolean putIfAbsent(K key, V value) YES NO YES
boolean remove(K key) NO 返回true时YES NO NO
boolean remove(K key, V oldValue) NO 返回true时YES NO NO
void removeAll() NO 每个被移除条目一次 NO NO
void removeAll(Set<? extends K> keys) NO 每个被移除条目一次 NO NO
boolean replace(K key, V value) YES NO YES YES
boolean replace(K key, V oldValue, V newValue) YES NO YES YES

可移植性建议

下面对于提升应用的可移植性的建议。

  1. 自定义的 Key 类需要实现 Object.hashcode()Object.equals() 方法,自定义的 Value 类需要正确覆写 Object.equals()
  2. 为了支持默认的 按值存储 的语义,自定义的 key 和 Value 类应该能够被序列化
  3. 缓存不使用正斜杠(/)或冒号(:)作为其名称的一部分。另外,建议缓存名称以java开头。或javax。不应使用。
  4. 当请求CacheManager时,应用程序使用默认的URI和属性。
  5. 应用程序避免使用规范的可选功能,或者使用CachingProvider.isSupported方法来利用可选功能(如果存在)。例如,按引用存储过程中的实现可能比按值存储具有更高的性能,因为键和值可以直接引用。
  6. 应用程序将专有配置保留在专有声明性配置文件中,而不是使用专有程序化缓存构造。
  7. 不使用CacheManager,Cache,Cache.Entry和CacheInvocationContext中的unwrap方法。它们分别用于访问Cache和Cache.Entry 的专用实现。使用专有的API会降低可移植性。
  8. 应用程序不对缓存架构进行任何假设。例如,假设监听器将在本地执行,并创建对本地应用程序类实例的依赖关系,则可能无法跨实现移植。
  9. 对于CAS操作,按引用存储将使用 equals()方法进行比较,但按值存储不一定。 确保在自定义值类型上的equals()实现考虑到所有非瞬态字段,以便对序列化形式的比较将得出与使用 equals()方法进行比较的结果相同的结果。
  10. 应用程序避免在 EntryProcessor,CacheEntryListener,CacheLoader,CacheWriter或ExpiryPolicy的实现中对Cache和CacheManager方法进行引用和调用。重入算法可能导致不可预测的应用程序行为,包括运行时异常,死锁和/或无限制的资源消耗。

参考

  1. JSR107 Specification 1.1.1 Maintenance Release - Google Doc
  2. jsr107/jsr107spec - GitHub
  3. jsr107 - jcp.org

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Tornado

Introduction to Tornado

Michael Dory、Adam Parrish、Brendan Berg / O'Reilly Media / 2012-3-28 / USD 23.99

Tornado is a scalable, non-blocking web server and web application framework written in Python. It is also light-weight to deploy, fun to write for, and incredibly powerful. Tornado was written with p......一起来看看 《Introduction to Tornado》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具