SpringBoot 整合ehcache 3.x

SpringBoot 整合ehcache 3.x

注:如果您发现任何不正确的内容,或者您想要分享有关本文主题的更多信息,请撰写评论或联系我 15340938921@163.com。

以下环境基于 windows 10 java version “1.8.0_171” 代码编辑器为idea

文中代码可能不太严谨,仅供研究测试使用!

背景

​ 在编写后台项目过程中,遇到后台某个页面会检索大量数据到页面,导致页面反应速度变慢,且这些数据属于变动不频繁的(很少有删、改、增之类的操作),为了更好的提供服务,以最快的速度展示数据,所以想到了添加缓存。

​ 项目使用的是springBoot,就就怎么方便怎么来吧。用spring官方的总比自己写的能硬、更坚挺、更持久!

Cache Abstraction,自spring3.1开始支持像开启事务那样非侵入的添加缓存。spring原话如下

从3.1版开始,Spring框架就支持向现有Spring应用程序透明地添加缓存。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,对代码的影响最小。

从Spring 4.1开始,在JSR-107注释和更多定制选项的支持下,缓存抽象得到了显著扩展。

是什么?为什么使用它

​ 这块解释的内容有点照抄spring,不过都是为了尽快学习掌握。

缓冲和缓存是什么鬼

缓冲

缓冲区就像你在网上看小视频,比如pornhub之类的,视频需要加载速度,你看的是4K的。而你又很猴急,

​ 4K,是一种高清显示技术。主要应用于电视行业、电影行业、手机行业等。作为电视行业显示技术的革命性突破,4K已经成为行业内的常青树,热度从2012年开始就一直是有增无减。画质技术作为电视的核心要素,与3D、多屏互动等技术相比,画质技术给人们带来的不是一时新鲜感,它是从本质上提升电视的表现力,让用户能够感受到最优秀的画质所带来的视觉盛宴。

这个时候,如果没有缓存的话,网站加载一帧你看一帧。这对于看小片快进的你来说很难受,但是有了缓存之后(还有各种技术),网站会直接加载一分钟(对于你来说是直接看到有十分钟,其实还是一帧一帧,攒到十帧才给你看),你就看的很爽了,直接就完事了。可能解释的不太好。。。看看spring的解释

​ 术语“缓冲区”和“缓存”往往可以互换使用。但是请注意,它们代表不同的东西。传统上,缓冲区用作数据在快实体和慢实体之间的中间临时存储。由于一方必须等待另一方(这会影响性能),缓冲区允许整个数据块(而不是小块)同时移动,从而缓解了这种情况。数据只从缓冲区写入和读取一次。此外,至少有一方知道缓冲区是可见的。

另一方面,缓存根据定义是隐藏的,并且任何一方都不知道缓存的发生。它还提高了性能,但是通过让相同的数据以快速方式多次读取来提高性能。

抽象

抽象缓存 是基于方法的,会保存方法的入参以及其结果,如果下次有相同的请求过来直接返回已经存储的结果(在没有清空缓存的情况下)。

​ 缓存抽象的核心是将缓存应用于Java方法,从而根据缓存中可用的信息减少执行的次数。也就是说,每次调用目标方法时,抽象都会应用缓存行为,检查方法是否已经为给定的参数执行。如果已执行,则返回缓存的结果,而不必执行实际的方法。如果方法尚未执行,则执行该方法,并缓存结果并返回给用户,以便在下次调用该方法时返回缓存的结果。这样,对于给定的一组参数,昂贵的方法(无论是CPU绑定的还是io绑定的)只能执行一次,并且结果可以重用,而不必实际再次执行该方法。缓存逻辑被透明地应用,没有任何对调用程序的干扰。

​ 这种方法只适用于保证为给定输入(或参数)返回相同输出(结果)的方法,无论执行了多少次。

​ 简单理解了缓存接下来进入正题。

什么是ehcache

官网

Ehcache是一种基于标准的开源缓存,可提高性能,卸载数据库并简化可伸缩性。它是使用最广泛的基于Java的缓存,因为它功能强大,经过验证,功能齐全,并与其他流行的库和框架集成。Ehcache可以从进程内缓存扩展到使用TB级缓存的混合进程内/进程外部署。

为什么使用ehcahe

​ 如果要你自己实现缓存你会如何选择,创建个自定义注解然后aop监听,将查询入参和结果放入ConcurrentHashMap抑或是redis,在对数据的update、insert、delete时删除缓存。你能想到的这些早有人做出来了,能用已经成熟的东西干嘛非要造轮子。

​ 至于为啥用ehcache,我顺手行不,用redis还得搭建redis,麻烦。。。。

怎么样

spring提供缓存功能,如果还没有定义类型为CacheManager的bean 或名为CacheResolver的CacheResolver的CacheResolver。Spring Boot尝试检测以下提供者(按照指定的顺序)

Generic
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
EhCache 2.x
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Simple
  • Generic

  • JCache

  • EhCache 2.x

  • Hazelcast

  • Infinispan

  • Couchbase

  • Redis

  • Caffeine

  • Simple

    如果找不到其他提供程序,则配置一个使用ConcurrentHashMap作为缓存存储的简单实现。如果应用程序中没有缓存库,这是默认设置 。至于怎么默认找到这个 SimpleCacheConfiguration 想要了解原理,你首先得搞明白springBoot 是如何自动配置的,现阶段知道是默认就行。有兴趣可以看看 org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration#cacheManager

我们先使用默认的 cache。 使用idea直接创建springBoot脚手架,并引入pom依赖

  • spring-boot-starter-cache

使用spring cache缓存

  1. 我的pom依赖如下,或者你可以去maven仓库找最新版本的ehcache。这个demo将会先做一个简单的演示,然后在进行深入探讨(先知道这玩意儿怎么用,达到什么效果,在解释怎么用,为什么这样用)。
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>
    
<dependencies>

        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
    </dependencies>
  1. 在 DemoApplication.java 类上加注解@EnableCaching 声明启用缓存。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching // 开启基于注解的缓存
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
  1. 新建一个简单的测试controller TestController.java(与 DemoApplication.java 同包,或者在其包包含范围内)
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController // @RequestMapping 和 @ResponseBody 
public class TestController {

    @RequestMapping("/{demoInt}")
    public String test(@PathVariable Integer demoInt) { // 接受路径的参数做乘法运算
        System.out.println("我要运算了~~~");
        return String.valueOf(demoInt * 666);
    }
}
  1. 启动项目。浏览器访问 http://127.0.0.1:8080/2 项目可以正常运行,接下来加入缓存。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController
public class TestController {

    @RequestMapping("/{demoInt}")
    // @Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。
    @Cacheable(value = "operation", // 缓存的名称,在 spring配置文件中定义,必须指定至少一个
               key = "#demoInt") // 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。
    public String test(@PathVariable Integer demoInt) {
        System.out.println("我要运算了~~~");
        return String.valueOf(demoInt * 666);
    }
    // 此处的入参一定要实现 java.io.Serializable 。 Integer类 继承抽象类 Number。 Number实现 Serializable
  1. 重新启动项目,第一次访问,浏览器打印
我要运算了~~~

​ 未重启,刷新页面第二次访问,控制台没有任何输出。这就说明缓存成功。

使用ehcache 3 缓存

  1. 修改pom,添加ehcache坐标,这里我们使用ehcache 3.X ,不使用 ehcache 2.x

    net.sf.ehcache(Ehcache 2.x)

    org.ehcache(Ehcache 3.x)

    我们需要spring-boot-starter-cache和cache-api依赖,以及依赖ehcache作为缓存提供者。

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.8.0</version>
</dependency>

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>

  1. 修改配置文件application.yml

    spring:
      cache:
        jcache: # 这里是 jcache 不是 ecache。我们使用的 ecache 3.x
          config: classpath:ehcache.xml
    
  2. 新建ehcache.xml

    <config
            xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
            xmlns='http://www.ehcache.org/v3'
            xsi:schemaLocation="
                http://www.ehcache.org/v3
                http://www.ehcache.org/schema/ehcache-core-3.7.xsd">
    
        <!-- 一个缓存模板。如果应用程序有多个缓存,但缓存的配置基本相同,那么这将特别有利。  -->
    
        <!-- 持久缓存目录 persistence(持久性标记) 在持久性标记中,我们为硬盘(磁盘存储)上基于文件的缓存定义了目录。这只是文件夹的定义。-->
        <persistence directory="spring-boot-ehcache/cache" />
    
       <!-- Default cache template -->
        <cache-template name="default">
            <expiry>
                <ttl unit="seconds">30</ttl> <!-- 在expires标签中,我们定义了30秒的生存时间(ttl)。生存时间指定缓存条目独立于访问可以在缓存中保留多长时间。指定的时间过期后,将从缓存中删除该值。 -->
            </expiry>
            <resources> <!-- 配置缓存的层和容量。 -->
                <heap>1000</heap> <!-- 对于堆上存储,我们配置了1,000个缓存条目的容量。这是开始删除缓存之前的最大条目数。 -->
                <offheap unit="MB">10</offheap> <!-- 对于堆外存储,我们配置了10mb的容量。 -->
                <disk persistent="true" unit="MB">20</disk> <!-- 作为磁盘缓存,我们配置了20mb 磁盘缓存必须始终具有比堆缓存更高的内存容量,否则应用程序在解析XML文件时在应用程序启动时抛出异常。 -->
            </resources>
        </cache-template>
    
    </config>
    
  3. 完成以上配置。实际上还不能够使用缓存。还需要在ehcache.xml 添加

    <!-- Cache configuration -->
        <cache alias="operation" uses-template="default"> <!-- 使用上面配置的模版 -->
    
        </cache>
    

    这里的 operation 是 TestController.test() 上@Cacheable注解的value值。 这里务必要加上。到这里基本就可以满足基本的要求。

如何添加缓存

​ 在指定需要添加缓存的方法上加上注释,此处加的注释与上文中SpringBoot使用默认的ConcurrentHashMap 相同

@RequestMapping("/{demoInt}")
    // @Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。
    @Cacheable(value = "operation",// 缓存的名称,在 spring配置文件中定义,必须指定至少一个
            key = "#demoInt") // 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。
    public String test(@PathVariable Integer demoInt) {
        // 此处的入参一定要实现 java.io.Serializable 。 Integer类 继承抽象类 Number。 Number实现 Serializable
        System.out.println("我要运算了~~~");
        return String.valueOf(demoInt * 666);
    }

​ 此处的 @Cacheable 的value值一定要在 ehcache.xml 中cache标签alias声明

<!-- Cache configuration -->
    <cache alias="operation" uses-template="default"> <!-- 使用上面配置的模版 -->

    </cache>

如何清除缓存

使用 @CacheEvict 注解

@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空 。

属性解释示例
allEntries是否清空所有缓存内容,缺省为 false,如果指定为true,则方法调用后将立即清空所有缓存@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存@CachEvict(value=”testcache”,beforeInvocation=true)

为了方便测试,我新建了一个对象,并重新编写了测试的Controller

import java.io.Serializable;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月12日
 */
public class Person implements Serializable { // 一定要实现 Serializable!!!!

    private static final long serialVersionUID = -680651108611576893L;
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController
public class TestController {

    @RequestMapping("/{demoInt}")
    // @Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。
    @Cacheable(value = "operation",// 缓存的名称,在 spring配置文件中定义,必须指定至少一个
            key = "#demoInt") // 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。
    public Object test(@PathVariable Integer demoInt) {
        // 此处的入参一定要实现 java.io.Serializable 。 Integer类 继承抽象类 Number。 Number实现 Serializable
        Person person = new Person();
        person.setAge(demoInt);
        person.setName("张三" + demoInt);
        System.out.println(person.toString());
        return person;
    }

    /**
     * I'm going to kill all the data
     * 方法调用后清空所有缓存
     */
    @RequestMapping("/all")
    @CacheEvict(value = "operation", allEntries = true)
    public String cleanAll() {
        return "SUCCESS";
    }

    /**
     * kill one data
     */
    @RequestMapping("/kill/{demoInt}")
    @CacheEvict(value = "operation", key = "#demoInt")
    public String cleanOne(@PathVariable Integer demoInt) {
        return "SUCCESS";
    }

更多支持的注解以及用法请看 spring 官网

参考

@Cacheable 参数

Annotation parameterDescription
value / cacheNames要存储方法执行结果的缓存的名称
keyThe key for the cache entries as Spring Expression Language (SpEL). If the parameter is not specified, a key is created for all method parameters by default.
keyGenerator实现密钥生成器接口的bean的名称,因此允许创建用户定义的缓存密钥。
conditionCondition as Spring Expression Language (SpEL) that specifies when a result is to be cached.
unlessCondition as Spring Expression Language (SpEL) that specifies when a result should not be cached.

参考链接

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值