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

 更新时间:2022年08月13日 09:17:35   作者:一只小熊猫呀  
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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 教你一步解决java.io.FileNotFoundException:找不到文件异常

    教你一步解决java.io.FileNotFoundException:找不到文件异常

    这篇文章主要给大家介绍了关于如何一步解决java.io.FileNotFoundException:找不到文件异常的相关资料,文中通过图文以及代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • 一篇文章带你深入了解javaIO基础

    一篇文章带你深入了解javaIO基础

    这篇文章主要介绍了java 基础知识之IO总结的相关资料,Java中的I/O分为两种类型,一种是顺序读取,一种是随机读取,需要的朋友可以参考下,希望对你有帮助
    2021-08-08
  • Java Management Extensions管理扩展原理解析

    Java Management Extensions管理扩展原理解析

    这篇文章主要介绍了Java Management Extensions管理扩展原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java Map 按Key排序实例代码

    Java Map 按Key排序实例代码

    这篇文章主要介绍了Java Map 按Key排序实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • @AutoConfigurationPackage与@ComponentScan注解区别

    @AutoConfigurationPackage与@ComponentScan注解区别

    这篇文章主要介绍了@AutoConfigurationPackage与@ComponentScan注解区别,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Java实现替换Word中文本和图片功能

    Java实现替换Word中文本和图片功能

    Word中的替换功能以查找指定文本然后替换为新的文本,可单个替换或全部替换。本文将用Java语言实现Word中的文本、图片替换功能,需要的可以参考一下
    2022-06-06
  • MyBatis缓存功能原理及实例解析

    MyBatis缓存功能原理及实例解析

    这篇文章主要介绍了MyBatis缓存功能原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Hibernate框架中的缓存技术详解

    Hibernate框架中的缓存技术详解

    这篇文章主要介绍了Hibernate框架中的缓存技术,结合实例形式详细分析了Hibernate框架缓存机制的原理与具体使用技巧,需要的朋友可以参考下
    2016-03-03
  • SpringBoot项目打包为JAR文件的实现

    SpringBoot项目打包为JAR文件的实现

    本文主要介绍了SpringBoot项目打包为JAR文件的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • Java可变个数形参的方法实例代码

    Java可变个数形参的方法实例代码

    这篇文章主要给大家介绍了关于Java可变个数形参的相关资料,文中通过图文以及实例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02

最新评论