SpringBoot 整合ehcache 3.x

SpringBoot 整合ehcache 3.x

文章目录

    • 背景
    • 是什么?为什么使用它
      • 缓冲和缓存是什么鬼
        • 缓冲
        • 抽象
      • 什么是ehcache
      • 为什么使用ehcahe
    • 怎么样
      • 使用spring cache缓存
      • 使用ehcache 3 缓存
      • 如何添加缓存
      • 如何清除缓存
    • 参考
      • @Cacheable 参数
      • 参考链接

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

以下环境基于 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将会先做一个简单的演示,然后在进行深入探讨(先知道这玩意儿怎么用,达到什么效果,在解释怎么用,为什么这样用)。

        org.springframework.boot
        spring-boot-starter-parent
        2.1.8.RELEASE
         

    


        
            org.springframework.boot
            spring-boot-starter-cache
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
    
  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作为缓存提供者。


    org.ehcache
    ehcache
    3.8.0



    javax.cache
    cache-api


  1. 修改配置文件application.yml

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

    
    
        
    
        
        
    
       
        
            
                30 
            
             
                1000 
                10 
                20 
            
        
    
    
    
  3. 完成以上配置。实际上还不能够使用缓存。还需要在ehcache.xml 添加

    
         
    
        
    

    这里的 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声明


     

    

如何清除缓存

使用 @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 parameter Description
value / cacheNames 要存储方法执行结果的缓存的名称
key The 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的名称,因此允许创建用户定义的缓存密钥。
condition Condition as Spring Expression Language (SpEL) that specifies when a result is to be cached.
unless Condition as Spring Expression Language (SpEL) that specifies when a result should not be cached.

参考链接

  • ehcache 3.8
  • Caching
  • Cache Abstraction
  • JCache (JSR-107) Annotations
  • Spring SpEL
  • Using Ehcache 3 in Spring Boot

你可能感兴趣的:(SpringBoot,ehcache)