Ehcache

Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It’s the most widely-used Java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks.

Ehcache详细

Quick Start

提供一个最简单的入门案例。

完整代码地址:cache-ehcache-hw

  • pom.xml

引入 jar

  [xml]
1
2
3
4
5
<dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.4.0</version> </dependency>
  • HelloWorld.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; public class HelloWorld { public static void main(String[] args) { CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("preConfigured", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) .build(); cacheManager.init(); Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class); Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)).build()); myCache.put(1L, "da one!"); String value = myCache.get(1L); System.out.println(value); preConfigured.put(1L, "preConfigured data"); String valueTwo = preConfigured.get(1L); System.out.println(valueTwo); System.out.println(myCache.get(1L)); cacheManager.removeCache("preConfigured"); cacheManager.close(); } }

运行结果:

  [plaintext]
1
2
3
4
5
6
7
8
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. da one! preConfigured data da one! Process finished with exit code 0

spring 整合

简单的和 spring 进行整合的例子。

完整代码地址:EhcacheTest.java

  • 项目结构
  [plaintext]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
├── cache-encache-spring.iml ├── pom.xml ├── src │   ├── main │   │   ├── java │   │   │   └── com │   │   │   └── ryo │   │   │   └── cache │   │   │   └── ehcache │   │   │   └── spring │   │   │   ├── model │   │   │   │   └── User.java │   │   │   └── service │   │   │   ├── UserService.java │   │   │   └── impl │   │   │   └── UserServiceImpl.java │   │   └── resources │   │   ├── applicationContext-cache.xml │   │   └── ehcache.xml │   └── test │   └── java │   └── com │   └── ryo │   └── cache │   └── ehcache │   └── spring │   ├── EhcacheTest.java │   └── service │   └── UserServiceTest.java
  • pom.xml
  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--cache--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.4</version> </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> <version>1.0.0</version> </dependency> <!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies>
  • applicationContext-cache.xml
  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 注解扫描路径 --> <context:component-scan base-package="com.ryo.cache.ehcache.spring"/> <!-- 支持缓存注解 --> <cache:annotation-driven cache-manager="cacheManager"/> <!-- 默认是cacheManager --> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="cacheManagerFactory"/> </bean> <!-- cache管理器配置 --> <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml"/> <property name="shared" value="true"/> </bean> </beans>
  • ehcache.xml
  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <!--timeToIdleSeconds 当缓存闲置n秒后销毁 --> <!--timeToLiveSeconds 当缓存存活n秒后销毁 --> <!-- 缓存配置 name:缓存名称。 maxElementsInMemory:缓存最大个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 maxElementsOnDisk:硬盘最大缓存个数。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 --> <!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir/easylink-mall-web/ehcache"/> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <!-- 用户信息数据缓存 数据缓存40分钟 --> <cache name="user-apply-cache" eternal="false" timeToIdleSeconds="2400" timeToLiveSeconds="2400" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> </cache> <!-- 自定义缓存,名称为 annotation.cache--> <cache name="annotation.cache" maxElementsInMemory="50" eternal="false" overflowToDisk="false" memoryStoreEvictionPolicy="LFU" /> </ehcache>
  • EhcacheTest.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import com.ryo.cache.ehcache.spring.model.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext-cache.xml") public class EhcacheTest { @Autowired private CacheManager cacheManager; @Test public void execute() { // 获取用户信息缓存容器 Cache cache = cacheManager.getCache("user-apply-cache"); Long id = 1L; User user = new User(id, "缓存测试"); // 将用户信息数据添加至缓存中 // key : id value : object cache.put(1L, user); // 获取用户信息数据 // 方法1 User cacheUser1 = (User) cache.get(id).get(); System.out.println(cacheUser1.getName()); // 方法2 User cacheUser2 = cache.get(id, User.class); System.out.println(cacheUser2.getName()); // 将用户信息数据从缓存中移除 cache.evict(id); System.out.println(cache.get(id, User.class)); } }

运行结果如下:

  [plaintext]
1
2
3
缓存测试 缓存测试 null

spring 缓存注解

完整代码示例:UserServiceTest.java

关于 cache 的支持,你可以阅读以下文档。

cache-jsr-107

  • 项目结构

和上面的一样

  • User.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.Serializable; public class User implements Serializable { private Long id; private String name; public User(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (id != null ? !id.equals(user.id) : user.id != null) return false; return name != null ? name.equals(user.name) : user.name == null; } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (name != null ? name.hashCode() : 0); return result; } }
  • UserService.java & UserServiceImpl.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import com.ryo.cache.ehcache.spring.model.User; import java.util.List; public interface UserService { /** * 创建用户 * @param id * @param name */ void createUser(final Long id, final String name); /** * 删除用户 * @param user */ void removeUser(User user); /** * 更新用户信息 * @param user */ void updateUser(User user); /** * 根据 ID 查询用户信息 * @param id 用户ID * @return */ User queryUser(final Long id); /** * 查询用户列表 * @return */ List<User> queryUserList(); }
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import com.ryo.cache.ehcache.spring.model.User; import com.ryo.cache.ehcache.spring.service.UserService; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.LinkedList; import java.util.List; @Service public class UserServiceImpl implements UserService { /** * 模仿数据库,暂时存储数据 */ private List<User> userList = new LinkedList<>(); //key为参数中User对象的id,缓存指定id对应的Employee对象 @Cacheable(value = "annotation.cache", key = "'create_' + #id") @Override public void createUser(Long id, String name) { System.out.println("创建用户:id: "+id+", name: "+name); User user = new User(id, name); userList.add(user); } //key为参数中User对象的id,缓存指定id对应的Employee对象 @Cacheable(value = "annotation.cache", key = "'query_' +#id") @Override public User queryUser(Long id) { System.out.println("查询用户:id: "+id); for(User user : userList) { if(user.getId().equals(id)) { return user; } } return null; } @CacheEvict(value = "annotation.cache", key = "'remove_'+#user.getId()") @Override public void removeUser(User user) { System.out.println("删除用户:user: "+user); if(userList.contains(user)) { int index = userList.indexOf(user); userList.remove(index); } else { System.out.println("未发现对应用户!"); } } //key为参数中User对象的id,缓存指定id对应的Employee对象 @CachePut(value = "annotation.cache", key = "'update_'+#user.id") @Override public void updateUser(User user) { System.out.println("更新用户:user: "+user); for(int i = 0; i < userList.size(); i++) { User original = userList.get(i); if(original.getId().equals(user.getId())) { userList.set(i, user); return; } } System.out.println("未发现对应用户!"); } @Override public List<User> queryUserList() { System.out.println("查询用户列表:"); return userList; } }
  • UserServiceTest.java

为了节约篇幅,每个测试的结果直接放在对应得到方法之上。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import com.ryo.cache.ehcache.spring.model.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext-cache.xml") public class UserServiceTest { @Autowired private UserService userService; // 查询用户列表: // [] // 创建用户:id: 1, name: 1L first time // 创建用户:id: 2, name: 2L first time // 查询用户列表: // [User{id=1, name='1L first time'}, User{id=2, name='2L first time'}] @Test public void createUserTest() { System.out.println(userService.queryUserList()); userService.createUser(1L, "1L first time"); userService.createUser(1L, "1L second time"); //第二次操作将不再进行 userService.createUser(2L, "2L first time"); System.out.println(userService.queryUserList()); } // 查询用户列表: // [] // 创建用户:id: 1, name: 1L first time // 创建用户:id: 2, name: 2L first time // 查询用户:id: 1 // User{id=1, name='1L first time'} // User{id=1, name='1L first time'} // 查询用户:id: 2 // User{id=2, name='2L first time'} @Test public void queryUserTest() { System.out.println(userService.queryUserList()); //1. 初始化数据 userService.createUser(1L, "1L first time"); userService.createUser(2L, "2L first time"); //查询用户 System.out.println(userService.queryUser(1L)); System.out.println(userService.queryUser(1L)); System.out.println(userService.queryUser(2L)); } // 查询用户列表: // [] // 创建用户:id: 1, name: 1L first time // 创建用户:id: 2, name: 2L first time // 更新用户:user: User{id=1, name='1L updated once'} // 查询用户:id: 1 // User{id=1, name='1L updated once'} // 更新用户:user: User{id=1, name='1L updated twice'} // User{id=1, name='1L updated once'} @Test public void updateUserTest() { System.out.println(userService.queryUserList()); //1. 初始化数据 userService.createUser(1L, "1L first time"); userService.createUser(2L, "2L first time"); //2. 更新数据 userService.updateUser(new User(1L, "1L updated once")); System.out.println(userService.queryUser(1L)); userService.updateUser(new User(1L, "1L updated twice")); System.out.println(userService.queryUser(1L)); } // 查询用户列表: // [] // 创建用户:id: 1, name: 1L first time // 创建用户:id: 2, name: 2L first time // 删除用户:user: User{id=1, name='1L first time'} // 查询用户:id: 1 // null // 删除用户:user: User{id=1, name='1L first time'} // 未发现对应用户! // null @Test public void removeUserTest() { System.out.println(userService.queryUserList()); //1. 初始化数据 userService.createUser(1L, "1L first time"); userService.createUser(2L, "2L first time"); //2. 删除数据 userService.removeUser(new User(1L, "1L first time")); System.out.println(userService.queryUser(1L)); userService.removeUser(new User(1L, "1L first time")); System.out.println(userService.queryUser(1L)); } }

spring boot 整合

完整代码地址:spring-boot-ehcache