Contents

Ehcache 简介

1. 概述

在本文中,我们将介绍Ehcache ,一种广泛使用的、基于 Java 的开源缓存。它具有内存和磁盘存储、侦听器、缓存加载器、RESTful 和 SOAP API 以及其他非常有用的特性。

为了展示缓存如何优化我们的应用程序,我们将创建一个简单的方法来计算所提供数字的平方值。在每次调用时,该方法将调用*calculateSquareOfNumber(int number)*方法并将信息消息打印到控制台。

通过这个简单的示例,我们想展示平方值的计算只进行一次,并且具有相同输入值的每个其他调用都从缓存中返回结果。

需要注意的是,我们完全专注于 Ehcache 本身(没有 Spring);如果您想了解 Ehcache 如何与 Spring 一起工作,请阅读这篇文章

2. Maven依赖

为了使用 Ehcache,我们需要添加这个 Maven 依赖项:

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

可以在此处 找到最新版本的 Ehcache 工件。

3. 缓存配置

Ehcache 可以通过两种方式进行配置:

  • 第一种方式是通过 Java POJO,所有配置参数都通过 Ehcache API 进行配置
  • 第二种方式是通过 XML 文件配置,我们可以根据提供的 schema 定义配置 Ehcache

在本文中,我们将展示这两种方法——Java 和 XML 配置。

3.1. Java 配置

本小节将展示使用 POJO 配置 Ehcache 是多么容易。此外,我们将创建一个帮助类以更轻松地配置缓存和可用性:

public class CacheHelper {
    private CacheManager cacheManager;
    private Cache<Integer, Integer> squareNumberCache;
    public CacheHelper() {
        cacheManager = CacheManagerBuilder
          .newCacheManagerBuilder().build();
        cacheManager.init();
        squareNumberCache = cacheManager
          .createCache("squaredNumber", CacheConfigurationBuilder
            .newCacheConfigurationBuilder(
              Integer.class, Integer.class,
              ResourcePoolsBuilder.heap(10)));
    }
    public Cache<Integer, Integer> getSquareNumberCacheFromCacheManager() {
        return cacheManager.getCache("squaredNumber", Integer.class, Integer.class);
    }
    
    // standard getters and setters
}

要初始化我们的缓存,首先,我们需要定义 Ehcache CacheManager对象。在这个例子中,我们使用newCacheManagerBuilder() API创建一个默认的缓存squaredNumber”

缓存将简单地将Integer键映射到Integer值。

请注意,在我们开始使用定义的缓存之前,我们需要使用init()方法初始化CacheManager对象。

最后,要获取我们的缓存,我们可以使用getCache() API 和我们提供的缓存名称、键和值类型。

通过这几行代码,我们创建了我们的第一个缓存,它现在可供我们的应用程序使用。

3.2. XML 配置

3.1 小节中的配置对象。等于使用这个 XML 配置:

<cache-template name="squaredNumber">
    <key-type>java.lang.Integer</key-type>
    <value-type>java.lang.Integer</value-type>
    <heap unit="entries">10</heap>
</cache-template>

为了在我们的 Java 应用程序中包含这个缓存,我们需要在 Java 中读取 XML 配置文件:

URL myUrl = getClass().getResource(xmlFile); 
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl); 
CacheManager myCacheManager = CacheManagerBuilder
  .newCacheManager(xmlConfig);

4. Ehcache测试

在第 3 节中,我们展示了如何为您的目的定义简单缓存。为了证明缓存确实有效,我们将创建SquaredCalculator类,该类将计算所提供输入的平方值,并将计算的值存储在缓存中。

当然,如果缓存中已经包含计算值,我们会返回缓存值,避免不必要的计算:

public class SquaredCalculator {
    private CacheHelper cache;
    public int getSquareValueOfNumber(int input) {
        if (cache.getSquareNumberCache().containsKey(input)) {
            return cache.getSquareNumberCache().get(input);
        }
        System.out.println("Calculating square value of " + input + 
          " and caching result.");
        int squaredValue = (int) Math.pow(input, 2);
        cache.getSquareNumberCache().put(input, squaredValue);
        return squaredValue;
    }
    //standard getters and setters;
}

为了完成我们的测试场景,我们还需要计算平方值的代码:

@Test
public void whenCalculatingSquareValueAgain_thenCacheHasAllValues() {
    for (int i = 10; i < 15; i++) {
        assertFalse(cacheHelper.getSquareNumberCache().containsKey(i));
        System.out.println("Square value of " + i + " is: "
          + squaredCalculator.getSquareValueOfNumber(i) + "\n");
    }      
    
    for (int i = 10; i < 15; i++) {
        assertTrue(cacheHelper.getSquareNumberCache().containsKey(i));
        System.out.println("Square value of " + i + " is: "
          + squaredCalculator.getSquareValueOfNumber(i) + "\n");
    }
}

如果我们运行我们的测试,我们将在控制台中得到这个结果:

Calculating square value of 10 and caching result.
Square value of 10 is: 100
Calculating square value of 11 and caching result.
Square value of 11 is: 121
Calculating square value of 12 and caching result.
Square value of 12 is: 144
Calculating square value of 13 and caching result.
Square value of 13 is: 169
Calculating square value of 14 and caching result.
Square value of 14 is: 196
Square value of 10 is: 100
Square value of 11 is: 121
Square value of 12 is: 144
Square value of 13 is: 169
Square value of 14 is: 196

如您所见,*calculate()*方法仅在第一次调用时进行计算。在第二次调用中,所有值都在缓存中找到并从中返回。

5. 其他 Ehcache 配置选项

当我们在前面的示例中创建缓存时,它是一个没有任何特殊选项的简单缓存。本节将显示在缓存创建中有用的其他选项。

5.1. 磁盘持久性

如果要存储到缓存中的值太多,我们可以将其中一些值存储在硬盘上。

PersistentCacheManager persistentCacheManager = 
  CacheManagerBuilder.newCacheManagerBuilder()
    .with(CacheManagerBuilder.persistence(getStoragePath()
      + File.separator 
      + "squaredValue")) 
    .withCache("persistent-cache", CacheConfigurationBuilder
      .newCacheConfigurationBuilder(Integer.class, Integer.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
          .heap(10, EntryUnit.ENTRIES)
          .disk(10, MemoryUnit.MB, true)) 
      )
  .build(true);
persistentCacheManager.close();

我们现在使用PersistentCacheManager代替默认的CacheManager ,它将持久化所有无法保存到内存中的值。 从配置中,我们可以看到缓存会将 10 个元素保存到内存中,并会在硬盘上分配 10MB 用于持久化。

5.2. 数据到期

如果我们缓存了很多数据,我们很自然地会把缓存的数据保存一段时间,这样我们就可以避免大量的内存使用。 Ehcache 通过Expiry接口控制数据的新鲜度:

CacheConfiguration<Integer, Integer> cacheConfiguration 
  = CacheConfigurationBuilder
    .newCacheConfigurationBuilder(Integer.class, Integer.class, 
      ResourcePoolsBuilder.heap(100)) 
    .withExpiry(Expirations.timeToLiveExpiration(Duration.of(60, 
      TimeUnit.SECONDS))).build();

在此缓存中,所有数据将存在 60 秒,在此时间段之后,它将从内存中删除。