JCache简介
JCache是JSR-107规范中定义了Java对象临时缓存在内存中的API和语义,包括对象的创建(object creation)、共享访问(shared access)、失效(invalidation)和跨JVM的一致性(consitency across jvm's)。
简单说就是:JCache是Java提供的标准缓存API。
JSR(Java Sepecification Requests),即Java规范提案。
下面是JSR-107规范中定义对于Java cache的定义。
JSR-107
核心概念
在JCache中定义了五个核心接口:CachingProvider、CacheManager、Cache、Entry和ExpiryPolicy。
- CachingProvider用于创建、配置、获取、管理和控制零个或多个CacheManager。应用程序在运行时可以访问或使用零个或多个CachingProvider。
- CacheManager用于创建、配置、获取、管理和控制零个或多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager只能归属一个CachingProvider。
- Cache是类似Map的数据结构,用于临时存储基于key的value。一个Cache只能归属一个CacheManager。
- Entry是存储在Cache中的键值对。
- ExpiryPolicy定义了每个Entry在Cache中有效时间(TTL),有效期内Entry能够被访问、修改和删除,有效期后该Entry不能在被访问、修改和删除。
对应关系如下:
值存储和引用存储
JSR-107中定义了两种Cache存储Entry的方法,即按值存储(Store-By-Value)和按引用存储(Store-By-Reference)。
- 按值存储是默认机制,也就是在存储kv对时,先对key和value进行拷贝,然后将拷贝的副本存储到Cache中。当访问Cache时,返回的是数据的副本。
- 按引用存储是另外一种可选的机制,存储kv对时,Cache中存储的是Key和Value的引用。当应用程序修改kv对时,应用程序无需再次修改Cache中的数据。
一致性
一致性是指当并发访问缓存时,需要保证修改的可见性在并发线程/进程间是一致的。为了保证一致性,所有的实现框架都应该支持以下默认一致性模型。
默认一致性模型
在执行大部分Cache操作时,就好像为Cache中的每个Key加了锁,当某个操作获取该key的排它性读写锁时,后面对该key的所有操作都会被阻塞,直到这个锁释放。注意,这里的操作可以是单个JVM进程内的,也可以是跨JVM的操作。
对于某些具有返回值的Cache操作,返回的缓存值需要是最新值。但是这个最新值,可以根据缓存的具体实现定义,比如当并发修改时,这个返回值可以是修改前的值,也可以是修改后的值。
Cache和Map的差异
Cache和Map两者之间有一些共性点,但是二者不是同一个东西。下面是二者之间的相同点与不同点:
相同点:
- 都是通过Key进行存储和访问。
- 每个Key都与一个Value对应。
- 当使用可变对象作为Key时,修改Key值可能引发查询对比的未知。
- 使用自定义类作为Key时,都需要显示实现Object.hashCode()方法。
不同点:
- Cache的Key和value不许为null,如果设置了null,则会抛出NullPointerException。
- Cache中的Entry可能会过期(Expire)。
- Cache中的Entry可能被驱逐(Evicted)。
- 为了支持原子比较和交换(compare-and-swap, CAP),Cache中的自定义应提供Object.equals()方法的实现。
- Cache中的Key和Value是需要可被序列化的。
- Cache可以设置使用值存储或引用存储来存储Entry实例。
- Cache是可以选择强制的安全性限制,如果违规操作,可以抛出SecurityException异常。
JCache demo
引入依赖:
<dependency><groupId>javax.cache</groupId><artifactId>cache-api</artifactId><version>1.1.1</version></dependency>
下面是JCache的使用demo。
CachingProviderprovider=Caching.getCachingProvider(); CacheManagercacheManager=provider.getCacheManager(); MutableConfiguration<String, Integer>config=newMutableConfiguration<>(); config.setTypes(String.class, Integer.class); config.setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(Duration.ONE_HOUR)); Cache<String, Integer>cache=cacheManager.createCache("testCache", config); cache.put("test1", 1); cache.put("test2", 2); System.out.println(cache.get("test1")); System.out.println(cache.get("test2")); cache.remove("test1"); System.out.println(cache.get("test2"));
需要注意的是我们没有提供实现框架,所以运行过程中会抛出以下异常,原因是JVM找不到getCacheManager()方法的任何实现。
Exceptioninthread"main"javax.cache.CacheException: NoCachingProvidershavebeenconfigured
源码分析
下面是JCache的源码,比较精简。
CachingProvider
CachingProvider是一个接口,我们可以用于创建和管理CacheManager的生命周期。
publicinterfaceCachingProviderextendsCloseable { CacheManagergetCacheManager(URIuri, ClassLoaderclassLoader, Propertiesproperties); ClassLoadergetDefaultClassLoader(); URIgetDefaultURI(); PropertiesgetDefaultProperties(); CacheManagergetCacheManager(URIuri, ClassLoaderclassLoader); CacheManagergetCacheManager(); voidclose(); voidclose(ClassLoaderclassLoader); voidclose(URIuri, ClassLoaderclassLoader); booleanisSupported(OptionalFeatureoptionalFeature); }
CacheManager
CacheManager是JCache API最重要的接口之一,他提供了创建、配置和销毁Cache的方法。
publicinterfaceCacheManagerextendsCloseable { CachingProvidergetCachingProvider(); URIgetURI(); ClassLoadergetClassLoader(); PropertiesgetProperties(); <K, V, CextendsConfiguration<K, V>>Cache<K, V>createCache(StringcacheName, Cconfiguration) throwsIllegalArgumentException; <K, V>Cache<K, V>getCache(StringcacheName, Class<K>keyType, Class<V>valueType); <K, V>Cache<K, V>getCache(StringcacheName); Iterable<String>getCacheNames(); voiddestroyCache(StringcacheName); voidenableManagement(StringcacheName, booleanenabled); voidenableStatistics(StringcacheName, booleanenabled); voidclose(); booleanisClosed(); <T>Tunwrap(java.lang.Class<T>clazz); }
Cache & Entry
Entry是Cache的内部接口:
publicinterfaceCache<K, V>extendsIterable<Cache.Entry<K, V>>, Closeable { Vget(Kvar1); Map<K, V>getAll(Set<?extendsK>var1); booleancontainsKey(Kvar1); voidloadAll(Set<?extendsK>var1, booleanvar2, CompletionListenervar3); voidput(Kvar1, Vvar2); VgetAndPut(Kvar1, Vvar2); voidputAll(Map<?extendsK, ?extendsV>var1); booleanputIfAbsent(Kvar1, Vvar2); booleanremove(Kvar1); booleanremove(Kvar1, Vvar2); VgetAndRemove(Kvar1); booleanreplace(Kvar1, Vvar2, Vvar3); booleanreplace(Kvar1, Vvar2); VgetAndReplace(Kvar1, Vvar2); voidremoveAll(Set<?extendsK>var1); voidremoveAll(); voidclear(); <CextendsConfiguration<K, V>>CgetConfiguration(Class<C>var1); <T>Tinvoke(Kvar1, EntryProcessor<K, V, T>var2, Object... var3) throwsEntryProcessorException; <T>Map<K, EntryProcessorResult<T>>invokeAll(Set<?extendsK>var1, EntryProcessor<K, V, T>var2, Object... var3); StringgetName(); CacheManagergetCacheManager(); voidclose(); booleanisClosed(); <T>Tunwrap(Class<T>var1); voidregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V>var1); voidderegisterCacheEntryListener(CacheEntryListenerConfiguration<K, V>var1); Iterator<Cache.Entry<K, V>>iterator(); publicinterfaceEntry<K, V> { KgetKey(); VgetValue(); <T>Tunwrap(Class<T>var1); } }
Configuration
配置类:
ExpiryPolicy
KV过期策略:
参考
https://www.jcp.org/en/jsr/detail?id=107
https://www.javadoc.io/doc/javax.cache/cache-api/1.0.0/javax/cache/package-summary.html
https://docs.oracle.com/middleware/1213/coherence/develop-applications/jcache_intro.htm#COHDG5778
https://docs.google.com/document/d/1ijduF_tmHvBaUS7VBBU2ZN8_eEBiFaXXg9OI0_ZxCrA/edit