聊聊Spring Cache的缓存抽象与JSR107缓存抽象JCache,并使用API方式使用Spring Cache【享学Spring】(下)

简介: 聊聊Spring Cache的缓存抽象与JSR107缓存抽象JCache,并使用API方式使用Spring Cache【享学Spring】(下)

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世界中的一个概念~~~


本文重在概念的介绍、核心接口类的理解。至于如何防止缓存穿透、缓存击穿、缓存雪崩和缓存刷新等高级话题,后面也会加以论述~


相关文章
|
2月前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
67 0
|
5月前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
7月前
|
缓存 NoSQL Java
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
325 0
|
5月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
1344 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
5月前
|
缓存 网络协议 API
【API管理 APIM】APIM中对后端API服务的DNS域名缓存问题
【API管理 APIM】APIM中对后端API服务的DNS域名缓存问题
|
5月前
|
存储 缓存 运维
平稳扩展:可支持RevenueCat每日12亿次API请求的缓存
平稳扩展:可支持RevenueCat每日12亿次API请求的缓存
53 1
|
5月前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
5月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
|
5月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之的Spring Boot中启用缓存支持问题如何解决
Java本地高性能缓存实践问题之的Spring Boot中启用缓存支持问题如何解决
|
7月前
|
缓存 NoSQL Java
后端开发中缓存的作用以及基于Spring框架演示实现缓存
后端开发中缓存的作用以及基于Spring框架演示实现缓存
60 1