SpringBoot浅析缓存机制之Ehcache 2.x应用

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。它是Hibernate中的默认缓存框架。Ehcache已经发布了3.1版本。但是本文的讲解基于2.x版本

介绍

Spring 3.1 中开始对缓存提供支持,核心思路是对方法的缓存,当开发者调用一个方法时,将方法的参数和返回值作为 key/value 缓存起来,当再次调用改方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法。但是,Spring 中并未提供缓存的实现,而是提供了一套缓存 API ,开发者可以自由选择缓存的实现,目前 Spring Boot 支持的缓存有如下几种:

  • JCache(JSR-107)
  • EhCache 2.x
  • Hazelcast
  • Infinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple

此处只介绍常用的缓存实现 Ehcache 2.x 和 Redis,由于 Spring 早已将缓存领域统一,因此无论使用哪种缓存实现,不同的只是缓存配置,开发者使用的缓存注解是一致的(Spring 缓存注解和各种缓存实现的关系就像 JDBC 和各种数据库驱动的关系一样)。

Ehcache 2.x 缓存

Ehcache 缓存在Java开发领域久负盛名,在Spring Boot 中,只需要一个配置文件就可以将 Ehcache 集成到项目中。步骤如下:

1. 创建项目添加缓存依赖

创建 Spring Boot 项目,添加 spring-boot-starter-cache 依赖以及 Ehcache 依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

2. 添加缓存配置文件

如果 Ehcache 的依赖存在,并且在 classpath 下有一个名为 echache.xml 的 Ehcache 配置文件,那么 EhCacheCacheManager 将会自动作为缓存的实现。因此,在 resources 目录下创建 ehcache.xml 文件作为 Ehcache 缓存的配置文件,如下:

<ehcache>
    <diskStore path="java.io.tmpdir/cache"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    <!--
        name:缓存名称
        maxElementsInMemory:缓存最大个数
        eternal:缓存对象是否永久有效。一旦设置了永久有效,timeout将不起作用
        timeToIdleSeconds:缓存对象在失效前允许闲置时间(秒),当eternal为false时生效
        timeToLiveSeconds:缓存对象在失效前允许存活的时间(秒),当eternal为false时生效
        overflowToDisk:当内存中的对象数量达到maxElementsInMemory时, Ehcache 是否将对象写到磁盘中
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔
    -->
    <cache name="book_cache"
           maxElementsInMemory="10000"
           eternal="true"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>
</ehcache>

这是一个常规的 Ehcache 配置文件,提供了两个缓存策略,一个是默认的,另一个名为 book_cache 。还有更为详细的 Ehcache 配置,此处不再一一介绍。如果开发者想自定义 Ehcache 配置文件的名称和位置,可以在 application.properties 中添加如下配置:

spring.cache.ehcache.config=classpath:ehcache2.xml

3. 开启缓存

在项目的入口类添加 @EnableCaching 注解开启缓存,如下

@SpringBootApplication
@EnableCaching
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}

4. 创建 BookDao

Book

public class Book implements Serializable {
    private Integer id;
    private String name;
    private String author;
    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}

BookDao

@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
    @Cacheable
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }
    @CachePut(key = "#book.id")
    public Book updateBookById(Book book) {
        System.out.println("updateBookById");
        book.setName("三国演义2");
        return book;
    }
    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById");
    }
}

代码解释:

在 BookDao 上添加 @CacheConfig 注解指明使用的缓存名字,这个配置可选,若不使用 @CacheConfig ,则直接在 @Cacheable 注解中指明缓存名字

在 getBookById 方法上添加 @Cacheable 注解表示对该方法进行缓存,默认情况下,缓存的key是方法的参数,缓存的 value 是方法的返回值。当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,则直接使用缓存数据,该方法不会执行,否则执行该方法,执行成功后将返回值缓存起来,但若是在当前类中调用该方法,则缓存不会生效

@Cacheable 注解中还有一个属性 condition 用来描述缓存的执行时机,例如 @Cacheable(“#id%2==0”) 表示 id 对 2 取模为0时才进缓存,否则不缓存

如果开发者不想使用默认到的 key ,也可以像 updateBookById 和 deleteBookById 一样自定义 key,@CachePut(key = “#book.id”) 表示缓存的key 为参数book 对象中 id 的值,@CacheEvict(key = “#id”)表示缓存的key为参数id。除了这种使用参数定义 key 的方式外,Spring 还提供了一个 root 对象用来生成 key ,如图

| 属性名称 | 属性描述 | 用法示例 |

| — | — | — |

| methodName | 当前方法名 | #root.methodName |

| method | 当前方法对象 | #root.method.name |

| caches | 当前方法使用的缓存 | #root.caches[0].name |

| target | 当前被调用的对象 | #root.target |

| targetClass | 当前被调用的对象的class | #root.targetClass |

| args | 当前方法参数数组 | #root.args[0] |

如果这些 key 不能满足开发需求,开发者也可以自定义缓存 key 的生成器 KeyGenerator,如下

@Component
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return Arrays.toString(params);
    }
}

然后在 @Cacheable 注解中引用 MyKeyGenerator 实例即可

@Service
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
    @Autowired
    MyKeyGenerator myKeyGenerator;
    @Cacheable(keyGenerator = "myKeyGenerator")
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }
    @CachePut(key = "#book.id")
    public Book updateBookById(Book book) {
        System.out.println("updateBookById");
        book.setName("三国演义2");
        return book;
    }
    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById");
    }
}

MyKeyGenerator 中的 generate 方法的参数分别是当前对象、当前请求的方法以及方法的参数,开发者可根据这些信息组成一个新的 key 返回,返回值就是缓存的 key。

  • @CachePut 注解一般用于数据更新方法上,与 @Cacheable 注解不同,添加了 @CachePut 注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来,如果 key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。同时 @CachePut 具有和 @Cacheable 类似的属性
  • @CacheEvict 注解一般用于删除方法上,表示移除一个 key 对应的缓存。@CacheEvict 注解由两个特殊属性:allEntries 和 beforeInvocation,其中 allEntries 表示是否将所有的缓存数据都移除,默认为 false,beforeInvocation 表示是否在方法执行之前移除缓存中的数据,默认为 false ,即在方法执行之后移除缓存中的数据 5. 创建测试类

5 .创建测试类

对 Service 中的方法进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class CacheApplicationTests {
    @Autowired
    BookDao bookDao;
    @Test
    public void contextLoads() {
        bookDao.deleteBookById(1);
        bookDao.getBookById(1);
        bookDao.getBookById(1);
        bookDao.deleteBookById(1);
        Book b3 = bookDao.getBookById(1);
        System.out.println("b3:"+b3);
        Book b = new Book();
        b.setName("三国演义");
        b.setAuthor("罗贯中");
        b.setId(1);
        bookDao.updateBookById(b);
        Book b4 = bookDao.getBookById(1);
        System.out.println("b4:"+b4);
    }
}

执行该方法,控制台打印日志如下:

deleteBookById
getBookById
deleteBookById
getBookById
b3:Book{id=1, name='三国演义', author='罗贯中'}
updateBookById
b4:Book{id=1, name='三国演义2', author='罗贯中'}

为了防止来回测试缓存的影响,这里先执行删除操作(同时也会删除缓存)。然后执行了一次查询,正常打印,接着又执行了一次查询没打印(直接读取的缓存),然后执行删除,接着再执行查询正常打印(删除操作也删除了缓存),再接着执行更新操作(同时更新了缓存),最后再次查询,打印更新后的数据。

到此这篇关于SpringBoot浅析缓存机制之Ehcache 2.x应用的文章就介绍到这了,更多相关SpringBoot Ehcache 2.x内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!

本文标题为:SpringBoot浅析缓存机制之Ehcache 2.x应用

基础教程推荐