JSR是Java Specification Requests 的缩写 ,Java规范请求 , JSR-107就是关于如何使用缓存的规范。
Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。
CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个 CachingProvider。
CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager 的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
Entry是一个存储在Cache中的key-value对。
Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条 目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
需要依赖jar包
<dependency>
<groupId>javax.cachegroupId>
<artifactId>cache-apiartifactId>
dependency>
需要提醒的是,该技术目前已被spring缓存抽象逐渐替代,如果想要更多了解的话,请访问官方文档
https://www.bookstack.cn/read/spring-5-framework-doc/32-32-4.md该处为spring文档中关于jsr107的部分介绍。
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;
每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
部分概念以及缓存注解
Cache | 缓存接口,定义缓存操作。 |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存 |
@EnableCaching | 开启基于注解的缓存 |
搭建SpringBoot基本运行环境并与Mybatis整合,在这里不进行赘述。
我们直接进行下列测试。
在对页面进行三次刷新之后我们可以看到的结果。我们可以看到并没有缓存的情况。
首先我们测试@Cacheable注解,该注解可对方法执行后的结果进行缓存。
@SpringBootApplication
@EnableCaching //开启缓存的注解支持
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
//在service层进行缓存测试
@Cacheable(value={"emp"})
public Employee getEmployeeById(Integer id){
System.out.println("开始查询");
return mapper.getElementById(id);
}
摘录cacheable的部分源码。
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};//指定缓存名称,便于缓存管理器(cachemanager)进行管理,唯一属性类似id
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";//缓存数据时的key,默认使用方法参数的值,也可使用spel表达式
String keyGenerator() default "";//key的生成器,与key二选一使用
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";//制定符合条件情况下进行缓存
String unless() default "";//与condition相反
boolean sync() default false;//是否使用异步模式
}
我们都知道,SpringBoot内部集成了大量的自动配置类,我们可以通过配置查看那个自动配置类执行了,在配置文件中输入
debug=true
可以打开配置报告。
这里只进行部分摘录
GenericCacheConfiguration:
Did not match:
- @ConditionalOnBean (types: org.springframework.cache.Cache; SearchStrategy: all) did not find any beans of type org.springframework.cache.Cache (OnBeanCondition)
Matched:
- Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition)
HazelcastCacheConfiguration:
Did not match:
JCacheCacheConfiguration:
Did not match:
SimpleCacheConfiguration matched: //该配置类在容器中注册了cachemanager(currentCacheManager)
可以获取和创建ConcurrentMapCache类型的缓存。该缓存将数据存入 map中
- Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
。。。。。。。。。。。
缓存的运行流程。
1.方法运行之前,去查询cache,按照指定名称进行获取。(获取缓存)
public Cache getCache(String name) {
Cache cache = (Cache)this.cacheMap.get(name);
2.第一次获取缓存,如果没有查找到,会创建Cache组件
if (cache == null && this.dynamic) {
synchronized(this.cacheMap) {
cache = (Cache)this.cacheMap.get(name);
if (cache == null) {
cache = this.createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
3.去cache中使用方法参数(key)查询缓存内容。
protected Object lookup(Object key) {
return this.store.get(key);
}
4.没有内容,调用方法。
5.将方法返回结果加入缓存。
key生成策略
public static Object generateKey(Object... params) {
if (params.length == 0) {//无参
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {//一个参数
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);//多参
}
}
s[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);//多参
}
}