最近猫哥在研究SpringBoot缓存,在这里对SpringBoot的Ehcache2.x缓存做一个总结梳理,顺便贴一下自己写的实战代码,以供参考。
SpringBoot缓存
Spring3.1中开始对缓存提供支持,核心思路是对方法的缓存,当开发者调用一个方法时,将方法的参数和返回值作为key/value缓存起来,当再次调用该方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法。但是,Spring中未提供缓存的实现,而是提供了一套缓存API,开发者可以自由选择缓存的实现,目前SpringBoot支持的缓存有如下几种:
- JCache(JSR-107)
- EnCache2.x
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple
这里主要介绍目前常用的缓存实现之一:EnCache2.x。由于Spring早已将缓存领域统一,因此无论使用哪种缓存实现,不同的只是缓存配置,开发者使用的缓存注解是一致的。
EnCache2.x缓存
在SpringBoot中,EnCache2.x缓存的使用十分常见,实现也比较容易,只需一个配置文件就可以将EnCache集成到项目中。EnCache2.x的使用具体步骤如下。
-
创建项目,添加依赖
创建Spring Boot项目,添加spring-boot-starter-cache依赖以及Encache依赖,代码如下:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
-
添加缓存配置文件
如果Encache的依赖存在,并且在classpath下面有一个名为encache.xml的Encache的配置文件,那么EnCacheCacheManager会自动作为缓存的实现。因此,在resources目录下创建encache.xml文件作为Encache缓存的配置文件,代码如下:
<?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="book_cache" maxElementsInMemory="10000" eternal="true" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskPersistent="true" diskExpiryThreadIntervalSeconds="600" /> </ehcache>
这是一个常规的Encache配置文件,提供了两个缓存策略,一个是默认的,一个是名为book_cache。另外,如果你想自定义Encache配置文件的名称和位置,可以在application.properties中添加如下配置:
spring.cache.ehcache.config=classpath:config/another-config.xml
-
开启缓存
在项目的入口类上添加@EnableCaching注解开启缓存,代码如下:@SpringBootApplication @EnableCaching public class MavenprojectApplication { public static void main(String[] args) { SpringApplication.run(MarvenprojectApplication.class, args); } }
-
创建Book实体类和BookService,代码如下:
public class BookBean implements Serializable { private Integer id; private String name; private String author; public Integer getId() { return id; } public String getName() { return name; } public String getAuthor() { return author; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAuthor(String author) { this.author = author; } }
@Repository @CacheConfig(cacheNames = "book_cache") public class BookDao { @Cacheable public BookBean getBookById(Integer id){ System.out.println("getBookById"); BookBean book = new BookBean(); book.setId(id); book.setName("三国演义"); book.setAuthor("罗贯中"); return book; } @CachePut(key = "#book.id") public BookBean updateBookById(BookBean book){ System.out.println("updateBookById"); book.setName("三国演义2"); return book; } @CacheEvict(key = "#id") public void deleteBookById(Integer id){ System.out.println("deleteBookById"); } }
ps:相对的@CacheEvict标注在需要清除缓存元素的方法或类上。 -
创建测试类
创建测试类,对Service中的方法进行测试,代码如下:
@RunWith(SpringRunner.class) @SpringBootTest public class CacheApplicationTests { @Autowired BookDao bookDao; @Test public void contextLoads(){ bookDao.getBookById(1); bookDao.getBookById(1); bookDao.deleteBookById(1); BookBean b3 = bookDao.getBookById(1); System.out.println("b3:"+b3.getName()); BookBean b = new BookBean(); b.setName("三国演义2"); b.setAuthor("罗贯中"); b.setId(1); bookDao.updateBookById(b); BookBean b4 = bookDao.getBookById(1); System.out.println("b4:"+b4.getName()); } }
ps:使用注解@RunWith(SpringRunner.class),需要首先在pom中引入依赖:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.2.RELEASE</version> <scope>compile</scope> </dependency>
-
最后执行测试类,得到打印日志
分析:一开始执行了两个查询,但是查询方法只打印了一次,因为第二次使用了缓存。接下来执行了删除方法,删除方法执行完之后再执行查询方法,查询方法又被执行了,因为在删除方法中缓存已经被删除了。再接下来执行了更新方法,更新方法中不仅更新了数据,也更新了缓存,所以在最后的查询方法中,查询方法日志没打印,说明该方法没执行,而是使用了缓存中的数据,而缓存中的数据已经被更新了。