Java緩存新標準(javax.cache)

jopen 10年前發布 | 32K 次閱讀 緩存 Java開發

這篇文章探索Java緩存的新標準:javax.cache。

怎么融入到Java生態系統(Java Ecosystem)

這個標準由JSR107所提出,它的作者同樣也是標準制定的領導者。JSR107由JSR342提出,已經被包含在Java EE 7中。Java EE7將會在2012年底定稿。但現在在Java SE6或更新的版本和Java EE 6環境中,甚至Spring和另外一些流行的開發環境中,javax.cache都可以正常使用。

JSR107當前正處于起草階段。我們當前最新的API發行版是0.3,同樣也有它的參照實現和TCK。文章中的代碼正是用的這個版本。

使用

專家團隊中比較活躍或者有興趣實現規范的廠商有:

  • Terracotta – Ehcache
  • Oracle – Coherence
  • JBoss – Infinispan
  • IBM – ExtemeScale
  • SpringSource – Gemfire
  • GridGain
  • TMax
  • Google App Engine Java
  • </ul>

    Terracotta將會根據最終的草稿發布一個Ehcache組件,在需要的情況下會更新。

    特性

    從設計的角度來看,基本的概念是一個CacheManager保存和控制一系列的緩存。緩存有很多條目(entries)。基本的API可以被當做是一個類似map并擁有下面一些特點的東西:

    • 原子操作,跟java.util.ConcurrentMap類似
    • 從緩存中讀取
    • 寫入緩存
    • 緩存事件監聽器
    • 數據統計
    • 包含所有隔離(ioslation)級別的事務
    • 緩存注解(annotations)
    • 保存定義key和值類型的泛型緩存
    • 引用保存(只適用于堆緩存)和值保存定義
    • </ul>

      可選特性

      我們通過另外一種方法,而不是把規范拆分成針對如Java SE和Spring/EE不同用戶群體的多個版本。

      首先,對Java SE方式的緩存,它是沒有任何依賴的。而對于Spring/EE你想要使用注解(annotations)和/或者事務,依賴將由這些框架提供。

      其次,我們提供了一個ServiceProvider.isSupported(OpertionalFeature feature)功能API,所以你可以在運行時決定實現的功能是什么。可選的特點如下:

      • storeByReference – 默認storeByValue
      • 事務性
      • 注解
      • </ul>

        這使得各個API實現可以在不用實現所有特性的情況下支持規范,并且允許終端用戶和框架去探索那些功能是什么,以便動態地配置合適的使用。

        對單個和分布式的緩存的好處

        雖然規范中沒有要求特別的分布式緩存結構,但實際上已經是默認緩存是分布的。我們有一個API可以覆蓋這兩種使用,但對分布式比較敏感。例如CacheEntryListener有一個它監聽的事件的NotificationScope,因此事件可以被限制在本地的傳遞。我們沒有高網絡消耗的類似map的如keySet()和values()的方法。并且,我們通常更傾向于不使用或使用低消耗的返回類型。所以Map有一個V put(K key, V value)javax.cache.Cache有一個void put(K key, V value)

        類加載

        緩存包含了供多個線程使用的數據,而這些線程又可能是運行在同一個JVM下的不同的容器應用或者OSGI組件,并且可能在一個集群中的多個JVM中。這使得類加載比較困難。

        我們設法解決了這個問題。在創建CacheManager時可以指定一個classloader。如果沒有指定,實現方式將會提供一個默認的。當對象被反序列化時,會使用CacheManager的classloader。

        這相比其他緩存如Ehcache等采用的后備方案是一個很大的進步。線程的上下文classloader首先被使用,如果它加載失敗,則會嘗試其他的classloader。這在大多數情況下都可以正常工作,但這還是有命中的問題,并且不同的實現方式有相當大的區別。

        獲取代碼

        標準的規范在Maven中文倉庫中。Maven的代碼段如下:

        <dependency>
             <groupId>javax.cache</groupId>
             <artifactId>cache-api</artifactId>
             <version>0.3</version>
        </dependency>

        API快速入門

        創建一個CacheManager

        我們也支持Java 6 java.util.ServiceLoader創建方式。它會自動檢測你的classpath中存在的緩存實現。接著,你可以創建通過下面的語句CacheManager

            CacheManager cacheManager = Caching.getCacheManager();

        它返回一個被稱做“default”單例CacheManager。在它之后的調用都會返回同一個CacheManager

        CacheManagers can have names and classloaders configured in. e.g.

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

        Implementations may also support direct creation with new for maximum flexibility:

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

        CacheManager可以通過下面的方式來配置名稱和classloader:

        CacheManager cacheManager =
            new RICacheManager("app1", Thread.currentThread().getContextClassLoader());</pre> <p>各個實現考慮到最好的靈活性也支持通過<code>new</code>進行直接的創建:</p>
        

            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");

        我們希望各個實現都可以擁有它們各自的用于方便配置CacheManager的為人熟知的配置文件。CacheManager的名稱可以用于區分各個配置文件。對于ehcache,這會是我們熟悉的位于classpath根目錄的ehcache.xml,但會帶上CacheManager的名稱和連接符作為前綴。所以,默認的CacheManager將會是ehcache.xml,而“myCacheManager”將會是app1-ehcache.xml。

        創建緩存

        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引入一系列標準的緩存注解,這些注解用于在依賴注入(dependency injection)容器中對注解類進行方法級別上的緩存攔截。緩存注解從Ehcache提供給Spring的注解開始正在變得越來越流行,而Ehcache的注解還影響了Spring 3的緩存注解。

        JSR 107注解包含了最基本的緩存操作,包括有:

        • @CacheResult – 使用緩存
        • @CachePut – 保存緩存
        • @CacheRemoveEntry – 從緩存中刪除單條記錄
        • @CacheRemoveAll – 刪除緩存中的所有記錄
        • </ul>

          但緩存名稱,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,還有展示了怎么通過@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){...}
          }</pre> <h3>和Spring結合使用</h3>
          

          對于Spring,key就是下面這行配置,它為Spring上下文添加緩存注解攔截器(caching annotation interceptors)。

          <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的實現類,通過在classpath目錄/META-INF/services定義名為javax.cache.annotation.BeanProvider的資源來告訴CDI應該去哪里找它。

          想要看如何跟CDI結合使用的例子,可以參考我們CDI測試用例中的CdiBeanProvider

          深入閱讀

          更深入的閱讀請訪問JSRs在https://github.com/jsr107/jsr107spec的主頁

          參考文檔:JCG成員Greg Luck的博客javax.cache:Java新緩存標準

          </blockquote> 原文鏈接: javacodegeeks 翻譯: ImportNew.com - 陳 曉舜
          譯文鏈接: http://www.importnew.com/11723.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!