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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • RabbitMq中channel接口的几种常用参数详解

    RabbitMq中channel接口的几种常用参数详解

    这篇文章主要介绍了RabbitMq中channel接口的几种常用参数详解,RabbitMQ 不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否己经断开,需要的朋友可以参考下
    2023-08-08
  • java实现在SSM下使用支付宝扫码支付功能

    java实现在SSM下使用支付宝扫码支付功能

    这篇文章主要为大家详细介绍了java实现在SSM下使用支付宝扫码支付功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • springboot 接收List 入参的几种方法

    springboot 接收List 入参的几种方法

    本文主要介绍了springboot 接收List 入参的几种方法,本文主要介绍了7种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • Spring AOP事务管理的示例详解

    Spring AOP事务管理的示例详解

    这篇文章将通过转账案例为大家详细介绍一下Spring AOP是如何进行事务管理的,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06
  • SpringCloud的Config配置中心详解

    SpringCloud的Config配置中心详解

    这篇文章主要介绍了SpringCloud的Config配置中心详解,SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置,需要的朋友可以参考下
    2023-07-07
  • Java调用计算机摄像头拍照实现过程解析

    Java调用计算机摄像头拍照实现过程解析

    这篇文章主要介绍了Java调用计算机摄像头拍照实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • SpringBoot实现登录拦截的示例代码

    SpringBoot实现登录拦截的示例代码

    如果我们不进行登录拦截的话,即使我们跳过登录页面直接去访问任意一个页面也能访问成功,那么登录功能就没有意义,同时也会存在安全问题,本文就来介绍一下SpringBoot登录拦截,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • spring中@Autowire和@Resource的区别在哪里(推荐)

    spring中@Autowire和@Resource的区别在哪里(推荐)

    这篇文章主要介绍了spring中@Autowire和@Resource的区别在哪里?本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • Java输出通过InetAddress获得的IP地址数组详细解析

    Java输出通过InetAddress获得的IP地址数组详细解析

    由于byte被认为是unsigned byte,所以最高位的1将会被解释为符号位,另外Java中存储是按照补码存储,所以1000 0111会被认为是补码形式,转换成原码便是1111 0001,转换成十进制数便是-121
    2013-09-09
  • 基于Mybatis-Plus拦截器实现MySQL数据加解密的示例代码

    基于Mybatis-Plus拦截器实现MySQL数据加解密的示例代码

    用户的一些敏感数据,例如手机号、邮箱、身份证等信息,在数据库以明文存储时会存在数据泄露的风险,因此需要进行加密,解密等功能,接下来本文就给大家介绍基于Mybatis-Plus拦截器实现MySQL数据加解密,需要的朋友可以参考下
    2023-07-07

最新评论