09-SpringBoot的缓存-1

09-SpringBoot的缓存-1

本文初概的介绍了SpringBoot的缓存,并介绍了EhCache的初级使用。更多深入的用法,请其他查看专业文档。

目录


image.png

1.什么是缓存

  • 一种以为键值对方式存在于内存的数据。一旦数据被缓存,用户的请求可不通过代码逻辑快速提供数据反馈。

  • 在键值对中,用LRU(last recently used)算法来保证对最近不常访问的数据进行清除

  • 由于存在内存中,通常一断电,缓存数据就Over了。

2.解决的问题

  • 提升性能

    主要用于读多写少的场景

  • 缓解数据库压力

    比如商城秒杀

3.使用缓存的场景

  • 数据实时性要求不高

  • 对性能要求比较高

4.SpringBoot中对缓存的支持

  • JCache

  • EhCache

  • Hazelcast

  • Infinispan

  • Couchbase

  • Redis

  • Caffeine

  • Simple

  • EhCache和Redis是目前比较主流的缓存使用方式

5. EhCache

单机,配置简单,快速上手,单机上性能好(JVM操作)。

劣势:拓展性性能较差。在集群方案中,基本看不到应用身影。

实现

本实现采用的SpringBoot版本信息如下:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.M4</version>
<relativePath/>
</parent>

下面我们来一步一步实现。

SpringBoot添加依赖

<dependencies>
    ……
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
    </dependency>
</dependencies>

SpringBoot缓存配置

缓存配置一般放在resources目下,名为一个ehcache.xml文件,如果没有,请自行创建。

修改配置文件位置

通常配置的位置在resouces目录下,如果要修改,请在application.properties中进行修改:

spring.cache.ehcache.config=classpath:config/another-config.xml
默认配置
    <defaultCache maxElementsInMemory="10000"
                   eternal="false"
                   timeToIdleSeconds="120"
                   timeToLiveSeconds="120"
                   overflowToDisk="false"
                   diskPersistent="false"
                   diskExpiryThreadIntervalSeconds="120"
    />

部分配置的说明:

​ name 缓存名唯一标识
​ maxElementsInMemory="1000" 内存中最大缓存对象数
​ eternal="false" 是否永久缓存
​ timeToIdleSeconds="3600" 缓存清除时间 默认是0 即永不过期
​ timeToLiveSeconds="0" 缓存存活时间 默认是0 即永不过期
​ overflowToDisk="true" 缓存对象达到最大数后,将其写入硬盘
​ maxElementsOnDisk="10000" 磁盘最大缓存数
​ diskPersistent="false" 磁盘持久化
​ diskExpiryThreadIntervalSeconds="120" 磁盘缓存的清理线程运行间隔
​ memoryStoreEvictionPolicy="FIFO" 缓存清空策略
​ FIFO 先进先出
​ LFU less frequently used 最少使用
​ LRU least recently used 最近最少使用

自定义配置

在这里定义一个DiskCache,代表缓存会存储在磁盘中:

    <cache name="DiskCache"
           maxEntriesLocalHeap="10000"
           eternal="true"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="120">
    </cache>

配置的全貌为:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir/cache"/>
    <!--默认缓存设置-->
    <defaultCache maxElementsInMemory="10000"
                   eternal="false"
                   timeToIdleSeconds="120"
                   timeToLiveSeconds="120"
                   overflowToDisk="false"
                   diskPersistent="false"
                   diskExpiryThreadIntervalSeconds="120"
    />
     
    <cache name="DiskCache"
           maxEntriesLocalHeap="10000"
           eternal="true"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="120">
    </cache>
</ehcache>

SpringBoot缓存启动定义

重在@EnableCaching标签

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

SpringBoot缓存功能实现

Dao实现

我们先来普及一下几个注解,如下:

@CacheConfig

在Dao类中进行声明,如果需要进行自定义类声明,需要配合CacheNames

@Cacheable

使用在方法的定义,标明是缓存方法,使用在查询方法上。

@CachePut

在使用update,以及insert方法的时候,需要使用这个注解,同时需要声明key,它代表了update的时候,key是使用哪一个(SQL上的术语)。

一般这么写@CachePut(key = "book.id"),book是方法入参的参数,在Update的时候,通常使用的是一个对象对吧。

这里有一个点需要注意,在定义@CachePut方法的时候,一定要将save对象返回,也就是方法return save对象,否则在后续get的时候,可能出现对象获取null的情况!切记切记。

@CacheEvict

在删除的时候使用,依然需要声明key,一般这样声明@CacheEvict(key = "#id"),id为我们需要删除对象的主键,也是我们删除方法的入参。

最后,来一个整体示例:

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;

import com.example.demo.beans.Book;

@Repository
@CacheConfig(cacheNames = "DiskCache")
public class BookDao {
    private Book mBook;
    /**
     *  初始化Book对象。方便做test
     * @return
     */
    public Book initBookCache() {
        mBook = new Book();
        mBook.setId(1);
        mBook.setName("西游记");
        mBook.setAuthor("罗贯中");
        return mBook;
    }
    
        
    @Cacheable
    public Book getBookById(Integer id) {
        System.out.println("getBookById:未使用缓存");
        if(id.equals(mBook.getId())) { // 模拟数据库查询
            return mBook;
        }
        return null;
    }
        
    @CachePut(key = "#mBook.id")
    public Book updateBookById(Book mBook) {
        System.out.println("updateBook:未使用缓存");
        this.mBook = mBook;  // 模拟数据库存储
        return mBook; // 请注意,这里一定要返回!否则缓存数据不会更新!!!!!
    }
    
    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById:未使用缓存");
        // 这里执行删除操作,同时使用@CacheEvict,将会把缓存中的数据也清除。
        // 删除后,调用getBookById,不会使用缓存。
    }

}
Bean实现

实现接口Serializabe

import java.io.Serializable;

public class Book implements Serializable{
        
    private static final long serialVersionUID = 1L;
    
    private Integer id;
    private String name;
    private String author;
    
    // ……getter/setter
}

SpringBoot测试

使用通常的测试即可,有如下注解需要了解

@RunWith(SpringRunner.class)

@Spring BootTest

@Autowired

@Test

@Before,@BeforeClass,@After,@AfterClass,@Igonore,都可以使用

在代码中,如果使用了缓存,是不会进入方法的,所以我们需要结合打印来理解。部分注释已经写在代码上了。

package com.example.demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.demo.beans.Book;
import com.example.demo.services.BookDao;

import static org.hamcrest.CoreMatchers.containsString;

import org.junit.Assert;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CacheTest {

    @Autowired
    BookDao mBookDao;

    @Test
    public void contextLoads() {
        mBookDao.initBookCache();

        // 查询测试
        mBookDao.getBookById(1); // 方法打印
        mBookDao.getBookById(1); // 使用缓存:不应打印

        // 删除测试
        mBookDao.deleteBookById(1); // 方法打印
        Book mBook = mBookDao.getBookById(1); // 若已经删除,这里再获取,应该打印未使用缓存

        // 升级测试
        Book mBook1 = new Book();
        mBook1.setId(1);
        mBook1.setName("西游记2");
        mBook1.setAuthor("罗贯中");
        mBookDao.updateBookById(mBook1);// 打印update
        mBook = mBookDao.getBookById(1); // 不应打印
        System.out.println(mBook.toString()); // 看到升級項
        Assert.assertThat(mBook.toString(),containsString("西游记2"));

        // insert测试
        mBook = mBookDao.getBookById(1);
        mBook.setId(10);
        mBookDao.updateBookById(mBook);
        mBook = mBookDao.getBookById(10); // 应打印
        System.out.println(mBook.toString()); 
        Assert.assertThat(mBook.toString(), containsString("id:10"));
    }
}

问题:

Eclipse中出现问题:no tests found with test runner 'JUnit 5'

解决:

问题在Eclipse中出现,问题在于需要解决JUnit5切换为4。解决办法是:在对应的项目,右键->Run Configurations ,左面选择JUnit,右面就会看到有个Test runner的选项,选择JUnit4。

提示不推荐使用Assert类型

解决:把import junit.framework.TestCase;的引入,改为import org.junit.Assert;

引用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Ehcache是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大,最初知道它,是从Hibernat...
    安易学车阅读 2,026评论 0 11
  • ---------------------------------------------------------...
    LAMYMAY阅读 729评论 0 3
  • 具体在Springboot中使用缓存如下: 1.在pom.xml中引入cache依赖,添加如下内容: 2.在Spr...
    crMiao阅读 5,158评论 0 4
  • 一、简介 Ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内...
    小程故事多阅读 43,799评论 9 59
  • 昨晚我买了一本诗集 丢了一台Kindle 我常常在自己身上 施展这种极其无趣的幽默感 既不好笑也不悲伤 如今我对失...
    浪荡马阅读 163评论 0 0