[翻譯]javax.cache: Java快取新標準

cpplover發表於2011-12-06

這篇文章將帶你 探索Java快取的新標準:javax.cache。

它是如何進入Java生態系統的?

這個標準是通過JSR107開發的。JSR107被包含在Java EE 7 中,由JSR342開發。Java EE 7將在2012年末完成。但是javax.cache將會同時作用於Java SE 6或更高版本,Java EE 6和Spring等其他流行環境。

JSR107還處於草案狀態。我們目前還處於0.3版本的API和參考實現。這篇文章的例子作用於這個版本。

實現

有專家團或者對實現規範感興趣的供應商如下:

Terracotta – Ehcache
Oracle – Coherence
JBoss – Infinispan
IBM – ExtemeScale
SpringSource – Gemfire
GridGain
TMax
Google App Engine Java

Terracotta將會為Ehcache釋出一個實現最終草案的javax.cache模組,然後根據javax.cache標準的最終版做出更新。

特性

從設計的角度看,基礎概念是一個裝載和控制一組快取(譯註:也可能是Cache物件)的CacheManager。基礎的API可以被看做是一種類似對映表,它還具有如下額外的特性:

原子操作,類似java.util.ConcurrentMap
讀快取
寫快取
快取事件監聽者
統計
包括所有等級的事務處理
快取註釋(譯註:Java的Annotation機制)
泛型快取
存引用和存值的定義

類的載入

快取包含的資料被不同的執行緒共享,這些執行緒可能執行在不同的容器或者OSGi的Bundle中,他們可能在一個JVM中,也可能被分佈在多個JVM的叢集中。這就使得類的載入需要具有技巧性。

我們已經解決了這個問題。當一個CacheManager建立時,我們需要指定一個classloader。如果沒有指定,我們就是用預設的。不管怎樣,物件的反序列化將會用到CacheManager的classloader。

相比Ehcache的落後方式,我們的處理方式是一個很大的進步。首先,執行緒的context classloader將會被使用,如果它失敗了,會嘗試使用另一個classloader。這樣做可以在大多數場景下有效,但是可能還需要根據具體實現做一些小調整和修改。

獲取程式碼

這個規範是以Maven為中心的。Maven配置片段如下

 <groupId>javax.cache</groupId>

 <artifactId>cache-api</artifactId>

 <version>0.3</version>

API使用簡介

建立一個CacheManager

我們支援Java 6 java.util.ServiceLoader的建立方式。它會自動在你的classpath裡偵測一個cache實現。然後你可以這樣建立一個CacheManager:

CacheManager cacheManager = Caching.getCacheManager();

這行程式碼返回一個CacheManager的單例物件,名為“default”。後續的呼叫都將返回相同CacheManager。

CacheManager物件可以有名字和classloader,參考如下:

CacheManager cacheManager = Caching.getCacheManager(“app1”, Thread.currentThread().getContextClassLoader());

實現或許支援直接new的方式來提供最大的靈活性:

CacheManager cacheManager = new RICacheManager(“app1”, Thread.currentThread().getContextClassLoader());

或者在不新增編譯依賴的情況實現同樣的工作:

String className = "javax.cache.implementation.RIServiceProvider";
Class<ServiceProvider> clazz =(Class<ServiceProvider>)Class.forName(className);
ServiceProvider provider = clazz.newInstance();
return provider.createCacheManager(Thread.currentThread().getContextClassLoader(), "app1");

建立一個快取

API支援程式設計方式建立快取。它補充了通常只能按照各供應商的約定,宣告式的配置快取方式。

讓我們通過程式配置一個名為“testCache”的只讀快取:

cacheManager = getCacheManager();
CacheConfiguration cacheConfiguration = cacheManager.createCacheConfiguration()
cacheConfiguration.setReadThrough(true);
Cache testCache = cacheManager.createCacheBuilder(“testCache”)
                    .setCacheConfiguration(cacheConfiguration).build();

獲取一個快取引用

你可以從CacheManager獲取快取。為了獲取一個名為testCache:

Cache<Integer, Date> cache = cacheManager.getCache(“testCache”);

基礎快取操作

放置一個快取:

Cache<Integer, Date> cache = cacheManager.getCache(cacheName);
Date value1 = new Date();
Integer key = 1;
cache.put(key, value1);

獲取一個快取:

Cache<Integer, Date> cache = cacheManager.getCache(cacheName);
Date value2 = cache.get(key);

刪除一個快取

Cache<Integer, Date> cache = cacheManager.getCache(cacheName);
Integer key = 1;
cache.remove(key);

註釋

JSR107介紹一個標準的快取操作集合,它們在一個執行在依賴注入容器裡的類中,完成方法級的快取注入 。由於Ehcache為Spring實現的註釋(而後影響了Spring 3快取註釋),註釋風格的快取正在變得越來越流行。
JSR107註釋包含的最常用的快取註釋包括:

@CacheResult – 使用cache
@CachePut – 放入cache
@CacheRemoveEntry – 從cache刪除單個條目
@CacheRemoveAll – 從cache刪除所有條目

當需要cache名字時,key和value都可以作為輸入。詳情查閱請JavaDoc。為了允許更精確的控制,你可以指定所有這些或更多。在下面的例子中,cacheName屬性可以被指定為“domainCache”,index可以作為key,domain可以作為value。

public class DomainDao {

 @CachePut(cacheName="domainCache")
 public void updateDomain(String domainId, @CacheKeyParam int index,
      @CacheValue Domain domain) {
 ...
 }
}

這個參考實現包含一個實現支援Spring和CDI。CDI在Java EE 6中的標準的容器驅動的注入器。這個實現 為支援重用,被很好的模組化了,同時使用Apache license,並且我們希望有更多開源快取能夠重用。雖然我們並沒有為Guice完成一個實現,但其實這是很容易完成的。

註釋例項

這個例子展示瞭如何實用註釋來保持一個快取與資料結構的同步(Blog manager的例子),同時使用快取來為response提速(通過使用@CacheResult)。

public class BlogManager {

@CacheResult(cacheName="blogManager")

public Blog getBlogEntry(String title) {...}

@CacheRemoveEntry(cacheName="blogManager")

public void removeBlogEntry(String title) {...}

@CacheRemoveAll(cacheName="blogManager")

public void removeAllBlogs() {...}

@CachePut(cacheName=”blogManager”)

public void createEntry(@CacheKeyParam String title,

@CacheValue Blog blog) {...}

@CacheResult(cacheName="blogManager")

public Blog getEntryCached(String randomArg,

@CacheKeyParam String title){...}

}

Spring例項

在Spring中,關鍵是下面的配置行,它把caching annotation interceptors(快取註釋攔截器)加入到Spring的context中:

<jcache-spring:annotation-driven proxy-target-class="true"/>

一個完成例子:

<beans ...>

<context:annotation-config/>

<jcache-spring:annotation-driven proxy-target-class="true"/>

<bean id="cacheManager" factory-method="getCacheManager" />

</beans>

Spring有它自己的快取註釋方式,它基於早期的JSR107貢獻者Eric Dalquist。那些註釋和JSR107將會很好的共存。

CDI例子

首先建立一個javax.cache.annotation.BeanProvider的實現,然後告訴CDI(在/META-INF/services/的classpath中宣告一個名為javax.cache.annotation.BeanProvider的資源)。
一個使用Weld實現的CDI,參考在我們CDI測試中的CdiBeanProvider

Further Reading 擴充套件閱讀

更多閱讀請訪問JSR的主頁https://github.com/jsr107/jsr107spec

檢視英文原文javax.cache: The new Java Caching Standard

本文參與iTran樂譯專案。

相關文章