前言
以前一直用Redis做缓存,但后来慢慢了解到了Ehcache,貌似Redis的名声早就超越了Ehcache,就很少看到有人提起Ehcache以及文章,但是,还是有必要了解一下的,说不定以后会用的上。更何况Ehcache也是Java领域中比较优秀的缓存,不知道小伙伴发现没,Ehcache正着念反着念,都是 Ehcache。
Ehcache简介
Ehcache是一个用Java实现的使用简单、高速、线程安全的缓存管理类库,提供了用内存、磁盘文件存储,以及分布式存储等管理方案。同时Ehcache作为开放源代码项目,采用限制比较宽松的Apache License V2.0作为授权方式,被广泛地用于Hibernate、Spring 等其他开源系统。
Ehcache的主要特点如下:
-
快速、简单。在过去众多的测试中已经表明Ehcache是最快的Java缓存之一,Ehcache的线程机制是为大型高并发系统设计的,Ehcache的API也易于使用,很容易部署上线和运行。
-
多种缓存策略。提供LRU、LFU和FIFO缓存策略。
-
缓存数据有两级。内存和磁盘,因此无须担心容量问题。缓存在内存和硬盘存储可以伸缩到GB
-
缓存数据会在虚拟机重启的过程中写入磁盘。Ehcache 是第一个引入缓存数据持久化存储的开源Java缓存框架,缓存的数据可以在机器重启后从磁盘上重新获得,可以通过cache.flush方法将缓存写入到磁盘上面。
-
可以通过RMI、 可插入API等方式进行分布式缓存。
-
具有缓存和缓存管理器的监听接口(CacheManagerEventListener、CacheEventListener)。
普通Java项目使用
到目前为止,ehcache最新版本是3.8.1,但是由于大部分的博客都低于这个版本,API可能有差异了,无法参考,所以还是到官网http://www.ehcache.org/documentation/3.8/getting-started.html下。
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.8.1</version>
</dependency>
另外ehcache使用SLF4J进行记录,如果是普通Java项目,还需要加入以下依赖。
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-nop</artifactId><version>1.7.2</version></dependency>
下面是代码示例。
public class Main {public static void main(String[] args) {/*** 构建CacheManager和一个Cache,别名为cache-1,键值对为Long和String*/CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("cache-1",CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))).build();cacheManager.init();/*** 获取别名为cache-1的Cache*/Cache<Long, String> cache1 =cacheManager.getCache("cache-1", Long.class, String.class);/*** 获取值,如果没有,则返回null*/System.out.println(cache1.get(1L));/*** put一个值*/cache1.put(1L,"缓存值");System.out.println(cache1.get(1L));/*** 通过createCache创建一个Cache*/cacheManager.createCache("cache-2",CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, User.class, ResourcePoolsBuilder.heap(10)));Cache<Long, User> cache2 =cacheManager.getCache("cache-2", Long.class, User.class);System.out.println(cache2.get(1L));cache2.put(1L,new User("张三"));System.out.println(cache2.get(1L).getName());}
}
我们也可以通过XML来配置。关于节点意思可以查看文档http://www.ehcache.org/documentation/3.8/xml.html。
<configxmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xmlns='http://www.ehcache.org/v3'xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd"><cache alias="cache-1"><key-type>java.lang.String</key-type><value-type>java.lang.String</value-type><resources><heap unit="entries">20</heap><offheap unit="MB">10</offheap></resources></cache>
</config>
只是构建CacheManager方式不同,其他API都一样。
public class XmlMain {public static void main(String[] args) {URL resource = XmlMain.class.getResource("/ehcache.xml");System.out.println(resource);Configuration xmlConfig = new XmlConfiguration(resource);CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);myCacheManager.init();Cache<String, String> cache2 =myCacheManager.getCache("cache-1", String.class, String.class);System.out.println(cache2);cache2.put("1","1");}
}
Spring Boot整合Ehcache
?首先要明确我们要做的事:实现计算某个数平方根的接口,如果此数小于10,则不进行缓存,否则将其缓存,并在5秒后过期。
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><version>2.2.2.RELEASE</version>
</dependency>
<dependency><groupId>javax.cache</groupId><artifactId>cache-api</artifactId><version>1.1.1</version>
</dependency>
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.8.1</version>
</dependency>
1.控制器
@RestController
public class TestController {private Logger log = LoggerFactory.getLogger(TestController.class);@Autowiredprivate NumberService numberService;@GetMapping(path = "/square/{number}")public String getSquare(@PathVariable Long number) {log.info("计算{}的平方", number);return String.format("{\"square\": %s}", numberService.square(number));}
}
2.Service
我们用@Cacheable注释该方法,让Spring处理缓存。Spring会创建NumberService的代理,拦截对square方法的调用并调用Ehcache。我们还需要提供要使用的缓存的名称以及key。condition是条件,此处条件意思是缓存number大于10的数。
@Service
public class NumberService {private Logger log = LoggerFactory.getLogger(NumberService.class);@Cacheable(value = "squareCache",key = "#number",condition = "#number>10")public BigDecimal square(Long number) {BigDecimal square = BigDecimal.valueOf(number).multiply(BigDecimal.valueOf(number));log.info("{}的平方是{}", number, square);return square;}
}
3.开启缓存
我们需要开启@EnableCaching。
@Configuration
@EnableCaching
public class EhcacheConfig {
}
也可以写到启动类上。
4.XML配置
我们还需要通过xml方式配置缓存。
在resources下创建ehcache.xml。
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.ehcache.org/v3"xmlns:jsr107="http://www.ehcache.org/v3/jsr107"xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsdhttp://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd"><cache alias="squareCache"><key-type>java.lang.Long</key-type><value-type>java.math.BigDecimal</value-type><expiry><ttl unit="seconds">5</ttl></expiry><listeners><listener><class>com.hxl.springbootdemo.list.CacheEventLogger</class><event-firing-mode>ASYNCHRONOUS</event-firing-mode><event-ordering-mode>UNORDERED</event-ordering-mode><events-to-fire-on>CREATED</events-to-fire-on><events-to-fire-on>EXPIRED</events-to-fire-on></listener></listeners><resources><heap unit="entries">2</heap><offheap unit="MB">10</offheap></resources></cache></config>
并在applicaion.properties下加入以下配置
spring.cache.jcache.config=classpath:ehcache.xml
5.监听器
另外还有一个监听器。
public class CacheEventLogger implements CacheEventListener<Object, Object> {private Logger log = LoggerFactory.getLogger(CacheEventLogger.class);@Overridepublic void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {log.info("旧值={},key={},新值={},type={}",cacheEvent.getOldValue(),cacheEvent.getKey(),cacheEvent.getNewValue(),cacheEvent.getType().name());}
}
运行之后在浏览器测试,当我们输入大于10的数字后,会将其缓存下来,过5秒后会过期,如果同样的数字在5秒内继续访问,则不会再次计算。
当输入小于10时,都不会缓存。