09-SpringBoot的緩存-1
本文初概的介紹了SpringBoot的緩存仆救,并介紹了EhCache的初級使用葫男。更多深入的用法,請其他查看專業(yè)文檔分苇。
目錄
1.什么是緩存
一種以為鍵值對方式存在于內(nèi)存的數(shù)據(jù)。一旦數(shù)據(jù)被緩存屁桑,用戶的請求可不通過代碼邏輯快速提供數(shù)據(jù)反饋医寿。
在鍵值對中,用LRU(last recently used)算法來保證對最近不常訪問的數(shù)據(jù)進行清除
由于存在內(nèi)存中蘑斧,通常一斷電靖秩,緩存數(shù)據(jù)就Over了。
2.解決的問題
-
提升性能
主要用于讀多寫少的場景
-
緩解數(shù)據(jù)庫壓力
比如商城秒殺
3.使用緩存的場景
數(shù)據(jù)實時性要求不高
對性能要求比較高
4.SpringBoot中對緩存的支持
JCache
EhCache
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Simple
EhCache和Redis是目前比較主流的緩存使用方式
5. EhCache
單機竖瘾,配置簡單沟突,快速上手,單機上性能好(JVM操作)捕传。
劣勢:拓展性性能較差惠拭。在集群方案中,基本看不到應(yīng)用身影。
實現(xiàn)
本實現(xiàn)采用的SpringBoot版本信息如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.M4</version>
<relativePath/>
</parent>
下面我們來一步一步實現(xiàn)职辅。
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文件,如果沒有域携,請自行創(chuàng)建簇秒。
修改配置文件位置
通常配置的位置在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" 內(nèi)存中最大緩存對象數(shù)
? eternal="false" 是否永久緩存
? timeToIdleSeconds="3600" 緩存清除時間 默認是0 即永不過期
? timeToLiveSeconds="0" 緩存存活時間 默認是0 即永不過期
? overflowToDisk="true" 緩存對象達到最大數(shù)后趋观,將其寫入硬盤
? maxElementsOnDisk="10000" 磁盤最大緩存數(shù)
? 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"/>
<!--默認緩存設(shè)置-->
<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緩存功能實現(xiàn)
Dao實現(xiàn)
我們先來普及一下幾個注解锋边,如下:
@CacheConfig
在Dao類中進行聲明皱坛,如果需要進行自定義類聲明,需要配合CacheNames
@Cacheable
使用在方法的定義宠默,標明是緩存方法麸恍,使用在查詢方法上。
@CachePut
在使用update搀矫,以及insert方法的時候抹沪,需要使用這個注解,同時需要聲明key
瓤球,它代表了update的時候融欧,key是使用哪一個(SQL上的術(shù)語)。
一般這么寫@CachePut(key = "book.id")
卦羡,book是方法入?yún)⒌膮?shù)噪馏,在Update的時候,通常使用的是一個對象對吧绿饵。
這里有一個點需要注意欠肾,在定義@CachePut方法的時候,一定要將save對象返回拟赊,也就是方法return save對象刺桃,否則在后續(xù)get的時候,可能出現(xiàn)對象獲取null的情況吸祟!切記切記瑟慈。
@CacheEvict
在刪除的時候使用,依然需要聲明key屋匕,一般這樣聲明@CacheEvict(key = "#id")
葛碧,id為我們需要刪除對象的主鍵,也是我們刪除方法的入?yún)ⅰ?/p>
最后过吻,來一個整體示例:
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())) { // 模擬數(shù)據(jù)庫查詢
return mBook;
}
return null;
}
@CachePut(key = "#mBook.id")
public Book updateBookById(Book mBook) {
System.out.println("updateBook:未使用緩存");
this.mBook = mBook; // 模擬數(shù)據(jù)庫存儲
return mBook; // 請注意,這里一定要返回!否則緩存數(shù)據(jù)不會更新T道拧U扯肌!K⑴邸翩隧!
}
@CacheEvict(key = "#id")
public void deleteBookById(Integer id) {
System.out.println("deleteBookById:未使用緩存");
// 這里執(zhí)行刪除操作,同時使用@CacheEvict呻纹,將會把緩存中的數(shù)據(jù)也清除堆生。
// 刪除后,調(diào)用getBookById雷酪,不會使用緩存淑仆。
}
}
Bean實現(xiàn)
實現(xiàn)接口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寞射,都可以使用
在代碼中,如果使用了緩存锌钮,是不會進入方法的桥温,所以我們需要結(jié)合打印來理解。部分注釋已經(jīng)寫在代碼上了梁丘。
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); // 使用緩存:不應(yīng)打印
// 刪除測試
mBookDao.deleteBookById(1); // 方法打印
Book mBook = mBookDao.getBookById(1); // 若已經(jīng)刪除侵浸,這里再獲取,應(yīng)該打印未使用緩存
// 升級測試
Book mBook1 = new Book();
mBook1.setId(1);
mBook1.setName("西游記2");
mBook1.setAuthor("羅貫中");
mBookDao.updateBookById(mBook1);// 打印update
mBook = mBookDao.getBookById(1); // 不應(yīng)打印
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); // 應(yīng)打印
System.out.println(mBook.toString());
Assert.assertThat(mBook.toString(), containsString("id:10"));
}
}
問題:
Eclipse中出現(xiàn)問題:no tests found with test runner 'JUnit 5'
解決:
問題在Eclipse中出現(xiàn)氛谜,問題在于需要解決JUnit5切換為4掏觉。解決辦法是:在對應(yīng)的項目,右鍵->Run Configurations 值漫,左面選擇JUnit履腋,右面就會看到有個Test runner的選項,選擇JUnit4惭嚣。
提示不推薦使用Assert類型
解決:把import junit.framework.TestCase;的引入,改為import org.junit.Assert;