针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。
CacheManger | 描述 |
SimpleCacheManager | 使用简单的Collection来存储缓存,主要用于测试 |
ConcurrentMapCacheManager | 使用ConcurrentMap作为缓存技术(默认),需要显式的删除缓存,无过期机制 |
NoOpCacheManager | 仅测试用途,不会实际存储缓存 |
EhCacheCacheManager | 使用EhCache作为缓存技术,以前在hibernate的时候经常用 |
GuavaCacheManager | 使用google guava的GuavaCache作为缓存技术(1.5版本已不建议使用) |
CaffeineCacheManager | 是使用Java8对Guava缓存的重写,spring5(springboot2)开始用Caffeine取代guava |
HazelcastCacheManager | 使用Hazelcast作为缓存技术 |
JCacheCacheManager | 使用JCache标准的实现作为缓存技术,如Apache Commons JCS |
RedisCacheManager | 使用Redis作为缓存技术 |
常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager
。SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。
使用
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.添加配置类
2.1 ConcurrentMapCacheManager,
spring默认就是ConcurrentMapCache,如果不指定缓存名称,可以不加配置类,直接@EnableCaching写道启动类上
@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Bean("ConcurrentMapCacheManager")
@Primary
public CacheManager caffeineCacheManager() {
ConcurrentMapCacheManager concurrentMapCacheManager = new ConcurrentMapCacheManager();
//可以事先指定chcheName
//Collection<String> cacheNames = concurrentMapCacheManager.getCacheNames();
//if (CollectionUtils.isEmpty(cacheNames)){
// 集合
// concurrentMapCacheManager.setCacheNames();
//}
return concurrentMapCacheManager;
}
}
2.2 caffeine
同理,如果不需要特殊参数,加入依赖后也可不写配置类
<!-- 使用 caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.0</version>
</dependency>
@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Bean("caffeineCacheManager")
@Primary
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 方案一(常用):定制化缓存Cache
cacheManager.setCaffeine(Caffeine.newBuilder()
// 缓存项在写入后的过期时间为 5 分钟。
.expireAfterWrite(5, TimeUnit.MINUTES)
//设置缓存的初始容量为 100 个缓存项。
.initialCapacity(100)
//设置缓存的最大容量为 200 个缓存项。
.maximumSize(200));
return cacheManager;
}
}
3.使用
主要基于Spring缓存注解@Cacheable、@CacheEvict、@CachePut的方式使用
- @Cacheable :改注解修饰的方法,若不存在缓存,则执行方法并将结果写入缓存;若存在缓存,则不执行方法,直接返回缓存结果。
- @CachePut :执行方法,更新缓存;该注解下的方法始终会被执行。
- @CacheEvict :删除缓存
3.1cacheable
调用这个方法的时候,会从缓存中查询,如果没有,查询数据库并将执行的结果存入缓存中,否则返回缓存中的对象。
参数 | 解释 | example |
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个,就是缓存的首个前缀 | 例如: |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @Cacheable(value=”room”,key=”#userId”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @Cacheable(value=room”,condition=”#userId > 2”) |
@Component
public class UserCache {
@Cacheable(cacheNames = "room", key = "#userId")
public User getUserId(Interger userId){
return queryuser();
}
}
3.2 CachePut
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
参数 | 解释 | example |
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | @CachePut(value=room”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,也可以使用返回值结果字段result | @CachePut(value=”room”,key=”#userId”) @CachePut(value=”room”,key=”#result.userId”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @CachePut(value=”room”,condition=”#userId>2”) |
@Component
public class UserCache {
@CachePut(cacheNames = "room", key = "#result.deptId+':'+#userId")
public User getUserId(String userId){
return queryuser();
}
}
3.3@CachEvict
主要针对方法配置,能够根据一定的条件对缓存进行清空
参数 | 解释 | example |
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | @CacheEvict(value=”my cache”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @CacheEvict(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @CacheEvict(value=”testcache”,condition=”#userName.length()>2”) |
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 | @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | @CachEvict(value=”testcache”,beforeInvocation=true) |
@Component
public class UserCache {
@CacheEvict(cacheNames = "room")
public User getUserId(String userId){
return null;
}
}
3.4 cacheManager的简单使用
//获取缓存
Cache user = cacheManager.getCache("room");
//获取所有缓存数据
User nativeCache = (User)user.getNativeCache();
//获取某个key的数据
Object o1 = user.get("1").get();
//存入数据
user.putIfAbsent(Object var1, @Nullable Object var2);
user.put(Object var1, @Nullable Object var2);
//清空数据
user.evictIfPresent("room");
spring cache源码
public interface Cache {
String getName();
Object getNativeCache();
@Nullable
ValueWrapper get(Object var1);
@Nullable
<T> T get(Object var1, @Nullable Class<T> var2);
@Nullable
<T> T get(Object var1, Callable<T> var2);
void put(Object var1, @Nullable Object var2);
@Nullable
default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
ValueWrapper existingValue = this.get(key);
if (existingValue == null) {
this.put(key, value);
}
return existingValue;
}
void evict(Object var1);
default boolean evictIfPresent(Object key) {
this.evict(key);
return false;
}
void clear();
default boolean invalidate() {
this.clear();
return false;
}
public static class ValueRetrievalException extends RuntimeException {
@Nullable
private final Object key;
public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) {
super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
this.key = key;
}
@Nullable
public Object getKey() {
return this.key;
}
}
@FunctionalInterface
public interface ValueWrapper {
@Nullable
Object get();
}
}
caffeine扩展的loadingCache
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.github.benmanes.caffeine.cache;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface LoadingCache<K, V> extends Cache<K, V> {
@Nullable V get(@NonNull K var1);
@NonNull Map<@NonNull K, @NonNull V> getAll(@NonNull Iterable<? extends @NonNull K> var1);
void refresh(@NonNull K var1);
}