SpringBoot Ehcache 2.x
一只小熊猫呀 人气:0介绍
Spring 3.1 中开始对缓存提供支持,核心思路是对方法的缓存,当开发者调用一个方法时,将方法的参数和返回值作为 key/value 缓存起来,当再次调用改方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法。但是,Spring 中并未提供缓存的实现,而是提供了一套缓存 API ,开发者可以自由选择缓存的实现,目前 Spring Boot 支持的缓存有如下几种:
- JCache(JSR-107)
- EhCache 2.x
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple
此处只介绍常用的缓存实现 Ehcache 2.x 和 Redis,由于 Spring 早已将缓存领域统一,因此无论使用哪种缓存实现,不同的只是缓存配置,开发者使用的缓存注解是一致的(Spring 缓存注解和各种缓存实现的关系就像 JDBC 和各种数据库驱动的关系一样)。
Ehcache 2.x 缓存
Ehcache 缓存在Java开发领域久负盛名,在Spring Boot 中,只需要一个配置文件就可以将 Ehcache 集成到项目中。步骤如下:
1. 创建项目添加缓存依赖
创建 Spring Boot 项目,添加 spring-boot-starter-cache 依赖以及 Ehcache 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2. 添加缓存配置文件
如果 Ehcache 的依赖存在,并且在 classpath 下有一个名为 echache.xml 的 Ehcache 配置文件,那么 EhCacheCacheManager 将会自动作为缓存的实现。因此,在 resources 目录下创建 ehcache.xml 文件作为 Ehcache 缓存的配置文件,如下:
<ehcache> <diskStore path="java.io.tmpdir/cache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- name:缓存名称 maxElementsInMemory:缓存最大个数 eternal:缓存对象是否永久有效。一旦设置了永久有效,timeout将不起作用 timeToIdleSeconds:缓存对象在失效前允许闲置时间(秒),当eternal为false时生效 timeToLiveSeconds:缓存对象在失效前允许存活的时间(秒),当eternal为false时生效 overflowToDisk:当内存中的对象数量达到maxElementsInMemory时, Ehcache 是否将对象写到磁盘中 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔 --> <cache name="book_cache" maxElementsInMemory="10000" eternal="true" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/> </ehcache>
这是一个常规的 Ehcache 配置文件,提供了两个缓存策略,一个是默认的,另一个名为 book_cache 。还有更为详细的 Ehcache 配置,此处不再一一介绍。如果开发者想自定义 Ehcache 配置文件的名称和位置,可以在 application.properties 中添加如下配置:
spring.cache.ehcache.config=classpath:ehcache2.xml
3. 开启缓存
在项目的入口类添加 @EnableCaching 注解开启缓存,如下
@SpringBootApplication @EnableCaching public class CacheApplication { public static void main(String[] args) { SpringApplication.run(CacheApplication.class, args); } }
4. 创建 BookDao
Book
public class Book implements Serializable { private Integer id; private String name; private String author; @Override public String toString() { return "Book{" + "id=" + id + ", name='" + name + '\'' + ", author='" + author + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
BookDao
@Repository @CacheConfig(cacheNames = "book_cache") public class BookDao { @Cacheable public Book getBookById(Integer id) { System.out.println("getBookById"); Book book = new Book(); book.setId(id); book.setName("三国演义"); book.setAuthor("罗贯中"); return book; } @CachePut(key = "#book.id") public Book updateBookById(Book book) { System.out.println("updateBookById"); book.setName("三国演义2"); return book; } @CacheEvict(key = "#id") public void deleteBookById(Integer id) { System.out.println("deleteBookById"); } }
代码解释:
在 BookDao 上添加 @CacheConfig 注解指明使用的缓存名字,这个配置可选,若不使用 @CacheConfig ,则直接在 @Cacheable 注解中指明缓存名字
在 getBookById 方法上添加 @Cacheable 注解表示对该方法进行缓存,默认情况下,缓存的key是方法的参数,缓存的 value 是方法的返回值。当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,则直接使用缓存数据,该方法不会执行,否则执行该方法,执行成功后将返回值缓存起来,但若是在当前类中调用该方法,则缓存不会生效
@Cacheable 注解中还有一个属性 condition 用来描述缓存的执行时机,例如 @Cacheable(“#id%2==0”) 表示 id 对 2 取模为0时才进缓存,否则不缓存
如果开发者不想使用默认到的 key ,也可以像 updateBookById 和 deleteBookById 一样自定义 key,@CachePut(key = “#book.id”) 表示缓存的key 为参数book 对象中 id 的值,@CacheEvict(key = “#id”)表示缓存的key为参数id。除了这种使用参数定义 key 的方式外,Spring 还提供了一个 root 对象用来生成 key ,如图
| 属性名称 | 属性描述 | 用法示例 |
| — | — | — |
| methodName | 当前方法名 | #root.methodName |
| method | 当前方法对象 | #root.method.name |
| caches | 当前方法使用的缓存 | #root.caches[0].name |
| target | 当前被调用的对象 | #root.target |
| targetClass | 当前被调用的对象的class | #root.targetClass |
| args | 当前方法参数数组 | #root.args[0] |
如果这些 key 不能满足开发需求,开发者也可以自定义缓存 key 的生成器 KeyGenerator,如下
@Component public class MyKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { return Arrays.toString(params); } }
然后在 @Cacheable 注解中引用 MyKeyGenerator 实例即可
@Service @CacheConfig(cacheNames = "book_cache") public class BookDao { @Autowired MyKeyGenerator myKeyGenerator; @Cacheable(keyGenerator = "myKeyGenerator") public Book getBookById(Integer id) { System.out.println("getBookById"); Book book = new Book(); book.setId(id); book.setName("三国演义"); book.setAuthor("罗贯中"); return book; } @CachePut(key = "#book.id") public Book updateBookById(Book book) { System.out.println("updateBookById"); book.setName("三国演义2"); return book; } @CacheEvict(key = "#id") public void deleteBookById(Integer id) { System.out.println("deleteBookById"); } }
MyKeyGenerator 中的 generate 方法的参数分别是当前对象、当前请求的方法以及方法的参数,开发者可根据这些信息组成一个新的 key 返回,返回值就是缓存的 key。
- @CachePut 注解一般用于数据更新方法上,与 @Cacheable 注解不同,添加了 @CachePut 注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来,如果 key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。同时 @CachePut 具有和 @Cacheable 类似的属性
- @CacheEvict 注解一般用于删除方法上,表示移除一个 key 对应的缓存。@CacheEvict 注解由两个特殊属性:allEntries 和 beforeInvocation,其中 allEntries 表示是否将所有的缓存数据都移除,默认为 false,beforeInvocation 表示是否在方法执行之前移除缓存中的数据,默认为 false ,即在方法执行之后移除缓存中的数据 5. 创建测试类
5 .创建测试类
对 Service 中的方法进行测试
@RunWith(SpringRunner.class) @SpringBootTest public class CacheApplicationTests { @Autowired BookDao bookDao; @Test public void contextLoads() { bookDao.deleteBookById(1); bookDao.getBookById(1); bookDao.getBookById(1); bookDao.deleteBookById(1); Book b3 = bookDao.getBookById(1); System.out.println("b3:"+b3); Book b = new Book(); b.setName("三国演义"); b.setAuthor("罗贯中"); b.setId(1); bookDao.updateBookById(b); Book b4 = bookDao.getBookById(1); System.out.println("b4:"+b4); } }
执行该方法,控制台打印日志如下:
deleteBookById
getBookById
deleteBookById
getBookById
b3:Book{id=1, name='三国演义', author='罗贯中'}
updateBookById
b4:Book{id=1, name='三国演义2', author='罗贯中'}
为了防止来回测试缓存的影响,这里先执行删除操作(同时也会删除缓存)。然后执行了一次查询,正常打印,接着又执行了一次查询没打印(直接读取的缓存),然后执行删除,接着再执行查询正常打印(删除操作也删除了缓存),再接着执行更新操作(同时更新了缓存),最后再次查询,打印更新后的数据。
加载全部内容