针对不同的缓存技术,需要实现不同的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")@Primarypublic 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")@Primarypublic CacheManager caffeineCacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();// 方案一(常用):定制化缓存CachecacheManager.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();@NullableValueWrapper 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);@Nullabledefault 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 {@Nullableprivate 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;}@Nullablepublic Object getKey() {return this.key;}}@FunctionalInterfacepublic interface ValueWrapper {@NullableObject 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);
}