缓存 可以提高访问速度,对高性能、高并发有一定的治疗效果。
缓存从存储位置可以分为 JVM级别缓存,进程级别缓存。JVM级别缓存优点:访问速度最快;缺点:不能实现分布式缓存,因为每个节点都是一个单独的JVM实例,所以各个节点的缓存不可见,会导致缓存不一致。进程级别缓存优点:保证缓存一致性,因为进程级别的缓存 相当于一个缓存服务器,各个节点都去同一个缓存服务器取数据,所以缓存一致性有保证。进程级别缓存缺点:IO消耗,相对慢于JVM级别缓存,但还是比数据库查询快。
Spring 对 cache 进行了抽象 并提供注解。但没有具体实现缓存。类似于JDBC接口的意思。定义了规范,让小面的厂商进行具体的实现。其中定义了两个主要的接口:缓存管理器接口:CacheManager 缓存接口:Cache
缓存管理器接口:CacheManager
接口很简单 只有两个方法:
public interface CacheManager {
@Nullable
Cache getCache(String name);
Collection getCacheNames();
}
Cache getCache(String name);
此方法可以定义根据缓存名称获取缓存,此方法可以实现二级缓存。
Collection
getCacheNames();
此方法获取所有缓存名称。
cacheManager是spring获取cache的入口,所以你可以在getCache(String name) 方法中定义你的获取cache的实现逻辑。
public class MyCacheManager implements CacheManager {
private ConcurrentMap caches = new ConcurrentHashMap();
public MyCacheManager(String name, Cache cache) {
this.caches.put(name, cache);
}
@Override
public Cache getCache(String name) {
//更加缓存名字获取缓存
Cache cache = this.caches.get(name);
if (cache != null) {
return cache;
} else {
//如果指定的缓存名字没有找到 则新建一个ConcurrentMapCache 并保存在cachemanager中
ConcurrentMapCache newCache = new ConcurrentMapCache(name);
caches.put(name, newCache);
return newCache;
}
}
@Override
public Collection getCacheNames() {
return this.caches.keySet();
}
}
spring只是定义了缓存 并实现了几个常用的cache比如目前较为流行的缓存有conCurrentMapCache、ehcache、caffeine。
而redis的实现由redis厂商提供。
其中conCurrentMapCache由spring实现,在spring-context包中。ehcache、caffeine的实现放在了spring-context-support包下。
rediscache实现在 spring-data-redis包下
定义好cacheManager后将其加入spring容器管理。下面贴出缓存在spring配置文件中的配置有些bean是在接下来讲的内容。
配置好bean后 在cache注解中 都会有cacheManager的指定 比如@cacheable注解 就有cacheManager的指定
@Cacheable(cacheManager = "cacheManager", key = "#i", value = "userCache", condition = "#i>7", unless = "#i>8")
public String getList(int i) {
return "list server method :: "
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
cache接口
public interface Cache {
/**
* Return the cache name.
*/
String getName();
/**
* Return the underlying native cache provider.
*/
Object getNativeCache();
//获取缓存的主要方法
@Nullable
ValueWrapper get(Object key);
@Nullable
T get(Object key, @Nullable Class type);
@Nullable
T get(Object key, Callable valueLoader);
//添加缓存的主要方法
void put(Object key, @Nullable Object value);
@Nullable
default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
ValueWrapper existingValue = get(key);
if (existingValue == null) {
put(key, value);
}
return existingValue;
}
void evict(Object key);
default boolean evictIfPresent(Object key) {
evict(key);
return false;
}
void clear();
default boolean invalidate() {
clear();
return false;
}
@FunctionalInterface
interface ValueWrapper {
@Nullable
Object get();
}
@SuppressWarnings("serial")
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;
}
}
}
cache的自定义实现类
public class MapCaffeCache implements Cache {
// private ConcurrentMapCache mapCache = new ConcurrentMapCache("mapCache");
private com.github.benmanes.caffeine.cache.@NonNull Cache
当cacheManager和cache实现完成后就是具体的cache注解使用了。主要有@Cacheable @Cacheput @CacheEvict @Caching @CacheConfig。主要讲下@cacheable 其他注解设置都基本差不多。
@Cacheable用于获取缓存数据,如果缓存没有命中则执行被注解的方法 一般是执行数据库查询,并刚数据库查询结果放入缓存中,cacheable缓存注解主要有一下设置:
@Cacheable(cacheManager = "cacheManager", key = "#i", value = "userCache", condition = "#i>7", unless = "#i>8")
cacheManager :设置缓存管理器 相当于数据库中的库名称
value : 设置缓存名称 相当于数据库中的表
key:设置缓存键名称 缓存是一个key value存储方式 所以可以是查找用的
condition :设置满足条件 true则缓存 false不缓存
unless:设置不缓存条件 true不缓存 false 缓存
key = " 'key-name' " 写死一个key 即 key-name 就是key的名字。注意要用单引号
key="#id" 使用注解的方法中的参数 id 的值作为key ,id 是方法参数列表的名字
key="cacheKeyGenerator" cacheKeyGenerator为spring容器管理的自定义key生成器
实现KeyGenerator接口就可以自定义一个key生成方法
public class CacheKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 0) {
return "spring:cache:defaultKey";
} else {
return target.getClass().getSimpleName() + ":" + method.getName();
}
}
}