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

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.4.0</version>
</dependency>
  • HelloWorld.java
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();
    }

}

运行结果:

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

  • 项目结构
├── 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
<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 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 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
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));
    }
}

运行结果如下:

缓存测试
缓存测试
null

spring 缓存注解

完整代码示例:UserServiceTest.java

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

cache-jsr-107

  • 项目结构

和上面的一样

  • User.java
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
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();

}
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

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

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