Springboot与缓存cache
JSR107缓存规范
CachingProvider
CachingManager
Cache
Entry
Expiry ()
key() 指定缓存的Key的值,不指定默认使用方法参数的值
@CachePut 在目标方法之后调用,将返回结果添加到缓存中
@Cacheable 首先查询缓存,生成keyGenerator()
@CacheEvict 删除缓存,可以设置在调用方法前后执行
以上3个方法一般都加在Service层的党法上
cache自动配置原理
缓存服务类比其他服务应该是配置在一个自动配置类中CacheAutoConfiguration中,我们点进这个类
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
//这里向容器中注入了一个组件,我们点进这个组件CacheConfigurationImportSelector
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
//这是一个静态的内部类
static class CacheConfigurationImportSelector implements ImportSelector {
//选择让哪些缓存配置类生效,通过debug的形式,看到imports为如下值
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
//这里打个断点
return imports;
/**imports =
* 0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
* 1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
* 2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
* 3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
* 4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
* 5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
* 6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
* 7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
* 8 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
* 9 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
默认是只有SimpleCacheConfiguration 这个配置类生效(debug=true)
控制台打印如下:
SimpleCacheConfiguration matched:
- Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
- @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)
*/
}
}
//点进SimpleCacheConfiguration,这是一个配置类
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
//给容器中注入一个CacheManager缓存管理器
//每个cacheManager都是一个ConcurrentMapCacheManager实例
@Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
//从配置文件中获取所有的cache的name
List<String> cacheNames = this.cacheProperties.getCacheNames();
//如果cache为空,cacheManager重新设置缓存名,并用一个Map将(cacheName,Cache)管理起来
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return this.customizerInvoker.customize(cacheManager);
}
//然后我们继续点进ConcurrentMapCacheManager这个类,这个类是用来管理Caches的,点进getCache()方法
@Override
@Nullable
public Cache getCache(String name) {
// 通过传入的name在map中查找对应的Cache对象
Cache cache = this.cacheMap.get(name);
// 如果查找结果为空,或者这个name在ConcurrentMap中不存在
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
// 那么就用线程安全的模式,按照传入的name创建一个ConcurrentMapCache实例
cache = createConcurrentMapCache(name);
// 然后通过Manager的Map属性将新建的cache与name管理起来
this.cacheMap.put(name, cache);
}
}
}
// 最终获取到cache并返回出去
return cache;
//ConcurrentMapCache是默认返回的Cache类型,我们点进去看看
//在Cache中,所有的缓存数据都是以(k,v)类型进行存储的,用一个Map store 属性进行维护
public class ConcurrentMapCache extends AbstractValueAdaptingCache {
//获取缓存内容的方法
protected Object lookup(Object key) {
return this.store.get(key);
}
// 添加缓存内容的方法
@Override
public void put(Object key, @Nullable Object value) {
this.store.put(key, toStoreValue(value));
}
// 删除缓存内容的方法
@Override
public void evict(Object key) {
this.store.remove(key);
}
// 最后可以在各个方法内打个断点试一试了,用@Cacheable注解,然后在浏览器中访问2次 分别查看代码走向
常用的SpEL用到的原数据值
在@Cacheable()中可以添加以下属性
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表 如:@Cacheable(value={“cache1,cache2”}) | #root.root.cahches[0].name |
argument name | root object | 方法参数的名字,可以直接用#id、#a0、#p0调用,0代表索引 | #iban、#a0、#p0 |
result | root object | 方法执行的返回值,@Cacheable不能调用 | #result |
应用示例:
@Cacheable(name =“emp”,key="#id", ) 同时支持自定义keyGenerator(),用key就不用keyGenerator;