09-SpringBoot的缓存-1
本文初概的介绍了SpringBoot的缓存,并介绍了EhCache的初级使用。更多深入的用法,请其他查看专业文档。
目录
1.什么是缓存
一种以为键值对方式存在于内存的数据。一旦数据被缓存,用户的请求可不通过代码逻辑快速提供数据反馈。
在键值对中,用LRU(last recently used)算法来保证对最近不常访问的数据进行清除
由于存在内存中,通常一断电,缓存数据就Over了。
2.解决的问题
-
提升性能
主要用于读多写少的场景
-
缓解数据库压力
比如商城秒杀
3.使用缓存的场景
数据实时性要求不高
对性能要求比较高
4.SpringBoot中对缓存的支持
JCache
EhCache
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Simple
EhCache和Redis是目前比较主流的缓存使用方式
5. EhCache
单机,配置简单,快速上手,单机上性能好(JVM操作)。
劣势:拓展性性能较差。在集群方案中,基本看不到应用身影。
实现
本实现采用的SpringBoot版本信息如下:
org.springframework.boot
spring-boot-starter-parent
2.2.0.M4
下面我们来一步一步实现。
SpringBoot添加依赖
……
org.springframework.boot
spring-boot-starter-cache
net.sf.ehcache
ehcache
SpringBoot缓存配置
缓存配置一般放在resources目下,名为一个ehcache.xml文件,如果没有,请自行创建。
修改配置文件位置
通常配置的位置在resouces目录下,如果要修改,请在application.properties中进行修改:
spring.cache.ehcache.config=classpath:config/another-config.xml
默认配置
部分配置的说明:
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,代表缓存会存储在磁盘中:
配置的全貌为:
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;
引用
- Spring boot cache的Simple Cache更新数据后获取的缓存数据为null问题,感谢作者:hj楚ing
- Junit4中的新断言assertThat的使用方法,感谢作者:飘飘雪