天天看點

Java Cache 緩存方案詳解及代碼-Ehcache

作者:Doker多克

一、Spring緩存概念

Spring從3.1開始定義了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口來統一不同的緩存技術; 并支援使用 JCache(JSR-107) 注解簡化我們開發。

常用的緩存實作有 RedisCache 、EhCache、ConcurrentMapCache 、Guava Cache(谷歌)等。

Spring Cache 介紹

Spring Cache是一個架構,實作了基于注解的緩存功能,隻需要簡單的加一個注解,就能實作緩存功能

Spring Cache提供了一層抽象,底層可以切換不同的Cache實作,具體是通過CacheManager接口來統一不同的緩存技術

針對于不同的緩存技術需要實作不同的CacheManager:

CacheManager 描述
EhCacheCacheManager 使用EhCache作為緩存技術
GuavaCacheManager 使用Google的GuavaCache作為緩存技術
RedisCacheManager 使用Redis作為緩存技術

Spring Cache 常用注解

我們來介紹Spring Cache用于緩存的常用的四個注解:

注解 說明
@EnableCaching 開啟緩存注解功能
@Cacheable 在方法執行前先檢視緩存中是否存有資料,如果有資料直接傳回資料;如果沒有,調用方法并将傳回值存入緩存
@CachePut 将方法的傳回值放到緩存
@CacheEvict 将一條或多條從緩存中删除

在Spring項目中,使用緩存技術隻需要導入相關緩存技術的依賴包,并在啟動類上加上@EnableCaching開啟緩存支援即可

二、Ehcache介紹

EhCache 是一個純Java的程序内緩存管理架構,屬于開源的Java分布式緩存架構,主要用于通用緩存,Java EE和輕量級容器,是從 Hibernate 的緩存開始的。

目前版本已到了Ehcache 3.10,Ehcache 3引入了以下内容:

  • 改進的 API,利用 Java 泛型并簡化緩存互動,
  • 與javax.cache API (JSR-107)完全相容,
  • 堆下存儲功能,包括僅堆下緩存,
  • 開箱即用的Spring Caching和Hibernate內建,這要歸功于javax.cache支援

1、 Ehcache特性:

1、快速輕量: Ehcache 是最快的 Java 緩存之一,很小的 jar 包

2、伸縮性:緩存在記憶體和磁盤存儲可以伸縮到數 G

3、靈活性:Ehcache 1.2 具備對象 API 接口和可序列化 API 接口。

4、标準支援 Ehcache 提供了對 JSR107 JCACHE API 最完整的實作

5、可擴充性 監聽器可以插件化

6、應用持久化 在 VM 重新開機後,持久化到磁盤的存儲可以複原資料

官網:Ehcache

2、 Ehcache 的加載子產品清單

ehcache-core:API,标準緩存引擎,RMI 複制和 Hibernate 支援

ehcache:分布式 Ehcache,包括 Ehcache 的核心和 Terracotta 的庫

ehcache-monitor:企業級監控和管理

ehcache-web:為 Java Servlet Container 提供緩存、gzip 壓縮支援的 filters

ehcache-jcache:JSR107 JCACHE 的實作

ehcache-jgroupsreplication:使用 JGroup 的複制

ehcache-jmsreplication:使用 JMS 的複制

ehcache-openjpa:OpenJPA 插件

ehcache-server:war 内部署或者單獨部署的 RESTful cache server

ehcache-unlockedreadsview:允許 Terracotta cache 的無鎖讀

ehcache-debugger:記錄 RMI 分布式調用事件

Ehcache for Ruby:Jruby and Rails 支援

3、核心定義:

  • cache manager:緩存管理器,以前是隻允許單例的,不過現在也可以多執行個體了
  • cache:緩存管理器内可以放置若幹 cache,存放資料的實質,所有 cache 都實作了 Ehcache 接口
  • element:單條緩存資料的組成機關
  • system of record(SOR):可以取到真實資料的元件,可以是真正的業務邏輯、外部接口調用、存放真實資料的資料庫等等,緩存就是從 SOR 中讀取或者寫入到 SOR 中去的

Ehcache 支援的資料存儲包括:

  • 堆上存儲 - 利用 Java 的堆上 RAM 記憶體來存儲緩存條目。此層使用與 您的 Java 應用程式,所有這些應用程式都必須由 JVM 垃圾回收器掃描。您的 JVM 堆空間越多 利用,應用程式性能受垃圾回收暫停的影響就越大。這家商店是 速度極快,但通常是您最有限的存儲資源。
  • 堆外存儲 - 大小僅受可用 RAM 的限制。 不受 Java 垃圾回收 (GC) 的限制。 非常快,但比堆上存儲慢,因為在存儲和重新通路資料時,必須将資料移入和移出 JVM 堆。
  • 磁盤存儲 - 利用磁盤(檔案系統)存儲緩存條目。 這種類型的存儲資源通常非常豐富,但比基于 RAM 的存儲慢得多。至于所有使用磁盤的應用程式 存儲時,建議使用快速專用的磁盤來優化吞吐量。
  • 群集存儲 - 此資料存儲是遠端伺服器上的緩存。 遠端伺服器可以選擇具有故障轉移伺服器,以提供改進的高可用性。 由于群集存儲會因網絡延遲以及建立用戶端/伺服器一緻性等因素而帶來性能損失, 從本質上講,此層比本地堆外存儲慢。

開發執行個體:

引入依賴:

<dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.10.0</version>
    </dependency>             

配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="defaultCache">

    <diskStore path="../temp/bojun/ehcache" />

    <!-- 預設緩存配置. -->
    <defaultCache maxEntriesLocalHeap="100" eternal="false" timeToIdleSeconds="1800" timeToLiveSeconds="3600"
        overflowToDisk="false" maxEntriesLocalDisk="100000" />

    <cache name="SystemAuthorizingRealm" maxEntriesLocalHeap="2000"
           eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0"
           overflowToDisk="false" statistics="true">
    </cache>


    <cache name="shiro-activeSessionCache" maxEntriesLocalHeap="2000"
           eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0"
           overflowToDisk="false" statistics="true">
    </cache>

    <!-- 系統緩存 -->
    <cache name="sysCache" maxEntriesLocalHeap="100" eternal="false" overflowToDisk="false"/>

    <cache name="cmsCache" maxEntriesLocalHeap="3000" eternal="false" overflowToDisk="false"/>
    <cache name="captchaCache" maxEntriesLocalHeap="3000" timeToLiveSeconds="300" eternal="false" overflowToDisk="false"/>

    <!-- 使用者緩存 -->
    <cache name="userCache" maxEntriesLocalHeap="100" eternal="false" overflowToDisk="false"/>

    <!-- 工作流子產品緩存 -->
    <cache name="actCache" maxEntriesLocalHeap="100" eternal="false" overflowToDisk="false"/>

    <cache name="sys.config" maxEntriesLocalHeap="100" eternal="false" overflowToDisk="false"/>

    <!-- 系統活動會話緩存 -->
    <cache name="activeSessionsCache" maxEntriesLocalHeap="10000" overflowToDisk="false"
           eternal="false" timeToLiveSeconds="0" timeToIdleSeconds="0"
           diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/>
</ehcache>
           

配置類代碼:

@ConditionalOnProperty(name = "spring.cache.type", havingValue = "ehcache")
@Configuration
@EnableCaching//标注啟動緩存.
public class CacheConfig {

    /**
     * @param ehCacheManagerFactoryBean
     * @return
     */

    @Bean
    public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean){
        System.out.println("CacheConfiguration.ehCacheCacheManager()");
        return new EhCacheCacheManager(ehCacheManagerFactoryBean.getObject());
    }

    /*
     * 據shared與否的設定,
     * Spring分别通過CacheManager.create()
     * 或new CacheManager()方式來建立一個ehcache基地.
     */
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
        System.out.println("CacheConfiguration.ehCacheManagerFactoryBean()");
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
        cacheManagerFactoryBean.setShared(true);
        return cacheManagerFactoryBean;
    }

}           

工具類:

public class CacheUtils {
private static CacheManager cacheManager = SpringContextHolder.getBean(CacheManager.class);
    private static final String SYS_CACHE = "sysCache";

    /**
     * 擷取SYS_CACHE緩存
     *
     * @param key
     * @return
     */
    public static Object get(String key) {
        return get(SYS_CACHE, key);
    }

    /**
     * 擷取SYS_CACHE緩存
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public static Object get(String key, Object defaultValue) {
        Object value = get(key);
        return value != null ? value : defaultValue;
    }

    /**
     * 寫入SYS_CACHE緩存
     *
     * @param key
     * @return
     */
    public static void put(String key, Object value) {
        put(SYS_CACHE, key, value);
    }

    /**
     * 從SYS_CACHE緩存中移除
     *
     * @param key
     * @return
     */
    public static void remove(String key) {
        remove(SYS_CACHE, key);
    }

    /**
     * 擷取緩存
     *
     * @param cacheName
     * @param key
     * @return
     */
    public static Object get(String cacheName, String key) {
        if( getCache(cacheName).get(key) == null){
            return null;
        }else {
            return getCache(cacheName).get(key).get();
        }
    }

    /**
     * 擷取緩存
     *
     * @param cacheName
     * @param key
     * @param defaultValue
     * @return
     */
    public static Object get(String cacheName, String key, Object defaultValue) {
        Object value = get(cacheName, key);
        return value != null ? value : defaultValue;
    }

    /**
     * 寫入緩存
     *
     * @param cacheName
     * @param key
     * @param value
     */
    public static void put(String cacheName, String key, Object value) {
        getCache(cacheName).put(key, value);
    }

    /**
     * 從緩存中移除
     *
     * @param cacheName
     * @param key
     */
    public static void remove(String cacheName, String key) {
        getCache(cacheName).evict(key);
    }


    /**
     * 獲得一個Cache,沒有則顯示日志。
     *
     * @param cacheName
     * @return
     */
    private static Cache getCache(String cacheName) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            throw new RuntimeException("目前系統中沒有定義“" + cacheName + "”這個緩存。");
        }
        return cache;
    }
}           

測試代碼:

@Cacheable(key="'user_'+#id",value="userCache")
    public User getUserById(String id){     
        return userDao.findById(id);    }           

這是一個cache架構,可以根據需要引入不同的cache實作方案

大家好,歡迎來到Doker品牌,歡迎點贊和評論,您的鼓勵是我們持續更新的動力!更多資料請前往Doker 多克