AbstractValueAdaptingCache
// @since 4.2.2 出现得还是挺晚的~~~ public abstract class AbstractValueAdaptingCache implements Cache { private final boolean allowNullValues; protected AbstractValueAdaptingCache(boolean allowNullValues) { this.allowNullValues = allowNullValues; } // lookup为抽象方法 @Override @Nullable public ValueWrapper get(Object key) { Object value = lookup(key); return toValueWrapper(value); } @Nullable protected abstract Object lookup(Object key); // lookup出来的value继续交给fromStoreValue()处理~ 其实就是对null值进行了处理 // 若是null值就返回null,而不是具体的值了~~~ @Override @SuppressWarnings("unchecked") @Nullable public <T> T get(Object key, @Nullable Class<T> type) { Object value = fromStoreValue(lookup(key)); if (value != null && type != null && !type.isInstance(value)) { throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); } return (T) value; } // 它是protected 方法 子类有复写 @Nullable protected Object fromStoreValue(@Nullable Object storeValue) { if (this.allowNullValues && storeValue == NullValue.INSTANCE) { return null; } return storeValue; } // 提供给子类使用的方法,对null值进行转换~ 子类有复写 protected Object toStoreValue(@Nullable Object userValue) { if (userValue == null) { if (this.allowNullValues) { return NullValue.INSTANCE; } throw new IllegalArgumentException("Cache '" + getName() + "' is configured to not allow null values but null was provided"); } return userValue; } // 把value进行了一层包装为SimpleValueWrapper @Nullable protected Cache.ValueWrapper toValueWrapper(@Nullable Object storeValue) { return (storeValue != null ? new SimpleValueWrapper(fromStoreValue(storeValue)) : null); } }
显然该类是后来(Spring4.2.2)插入进来的专门对null值进行的处理。它提供了通用实现,来适配null值的问题。若你自定义Cache的实现,建议继承自此抽象类。
ConcurrentMapCache
public class ConcurrentMapCache extends AbstractValueAdaptingCache { private final String name; // 底层存储就是个java.util.concurrent.ConcurrentMap~~ private final ConcurrentMap<Object, Object> store; ... // 本处只写出这个最全的构造器 它是protected 的,其它的构造器是public的 // 说白了serialization这个类不允许外部知道,子类知道即可~~~ protected ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues, @Nullable SerializationDelegate serialization) { super(allowNullValues); Assert.notNull(name, "Name must not be null"); Assert.notNull(store, "Store must not be null"); this.name = name; this.store = store; this.serialization = serialization; } // 显然只有指定了序列化器,才有可能true:存储副本 public final boolean isStoreByValue() { return (this.serialization != null); } ... @Override public final ConcurrentMap<Object, Object> getNativeCache() { return this.store; } // 这是父类的抽象方法 @Override @Nullable protected Object lookup(Object key) { return this.store.get(key); } @Override @Nullable public <T> T get(Object key, Callable<T> valueLoader) { return (T) fromStoreValue(this.store.computeIfAbsent(key, r -> { try { return toStoreValue(valueLoader.call()); } catch (Throwable ex) { throw new ValueRetrievalException(key, valueLoader, ex); } })); } @Override public void put(Object key, @Nullable Object value) { this.store.put(key, toStoreValue(value)); } @Override @Nullable public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { Object existing = this.store.putIfAbsent(key, toStoreValue(value)); return toValueWrapper(existing); } @Override public void evict(Object key) { this.store.remove(key); } @Override public void clear() { this.store.clear(); } ... }
ConcurrentMapCache它是spring-context提供的内建唯一缓存实现,它是完全基于本地内存的。
Springboot默认使用的是SimpleCacheConfiguration,它配置的是ConcurrentMapCacheManager来实现缓存,因此对应Cache实现为ConcurrentMapCache
NoOpCache
NoOpCache配合NoOpCacheManager使用~
使用示例
上面介绍了spring-context自带的一些缓存管理器CacheManager实现以及缓存Cache实现。接下来是骡子是马,现在拉出来遛遛,用个案例介绍它的使用方式:
public static void main(String[] args) { CacheManager cacheManager = new ConcurrentMapCacheManager(); //使用ConcurrentMapCacheManager可以不用初始化指定,可以get的时候动态创建Cache //CacheManager cacheManager = new ConcurrentMapCacheManager("car"); // 即使我们上面没有放进去名字为car的Cache,此处也会帮我们自动生成~~~ Cache carCache = cacheManager.getCache("car"); // 向缓存里加数据 carCache.put("benz", "奔驰"); carCache.put("bmw", "宝马"); carCache.put("audi", "奥迪"); System.out.println(carCache.getClass()); //class org.springframework.cache.concurrent.ConcurrentMapCache // 从缓存里获取数据 System.out.println(carCache.get("benz").get()); //奔驰 System.out.println(carCache.get("benz", String.class)); //奔驰 }
此处我们用ConcurrentMapCacheManager作为实例,其实还可以使用SimpleCacheManager这个最为简单的缓存管理器:
public static void main(String[] args) { CacheManager cacheManager = new SimpleCacheManager(); Cache car = cacheManager.getCache("car"); System.out.println(car); //null }
可以看到若我们使用SimpleCacheManager
这个缓存管理器,它并不会给我们动态生成Cache对象,一切都变成手动档了:
public static void main(String[] args) { SimpleCacheManager cacheManager = new SimpleCacheManager(); ConcurrentMapCache carMapCache = new ConcurrentMapCache("car"); cacheManager.setCaches(Collections.singleton(carMapCache)); cacheManager.afterPropertiesSet(); // 这一步是必须的 Cache car = cacheManager.getCache("car"); System.out.println(car); //org.springframework.cache.concurrent.ConcurrentMapCache@19469ea2 }
我们手动准备Cache、手动调用afterPropertiesSet()才有用~
本例只介绍了单元测试时的使用方式,若和Spring集成,一切就更简单了,各位小伙伴自行实践吧~
CompositeCacheManager + NoOpCacheManager
CompositeCacheManager主要用于集合多个CacheManager实例,在使用多种缓存容器(比如Redis+EhCache的组合)时特别有用。
设想这一个场景:当代码中使用@Cacheable注解指定的cacheNames中,却没有这个cacheManagers时,执行时便会报错。但是若此时我们使用的是CompositeCacheManager并且设置fallbackToNoOpCache=true,那么它就会没找到也最终进入到NoOpCacheManager里面去(用NoOpCache代替~),此时就相当于禁用掉了缓存,而不抛出相应的异常。
当你的应用中使用到了多个缓存的时候,强烈建议使用CompositeCacheManager管理(当然倘若是一个缓存也可以使用它,方便日后更加方便的扩展,这点在缓存注解章节里有深入讲解~)
最后需要注意的是:如果需要让Spring容器中的缓存可以正常工作,必须配置至少一个CacheManager。
总结
本文介绍了JSR107的缓存抽象JCache的概念和设计,以及重点介绍了Spring对缓存的抽象,希望各位看官在实操过程中,也需要注重一定概念性东西,更需要关注一下业界规范。需要注意的是,缓存不是Java世界中的一个概念~~~
本文重在概念的介绍、核心接口类的理解。至于如何防止缓存穿透、缓存击穿、缓存雪崩和缓存刷新等高级话题,后面也会加以论述~