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


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


相关文章
|
4天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
23 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
3月前
|
存储 缓存 测试技术
4个所有开发人员都应该知道的关键API缓存实践
4个所有开发人员都应该知道的关键API缓存实践
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
3月前
|
缓存 NoSQL Java
Spring Cache 缓存原理与 Redis 实践
Spring Cache 缓存原理与 Redis 实践
149 0
|
3月前
|
缓存 NoSQL Java
微服务框架(十二)Spring Boot Redis 缓存
  此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现。 本文为Spring Boot集成Redis。 在这篇文章中,我们将配置一个Spring Boot应用程序示例,并将其与Redis Cache 集成。虽然Redis是一个开源是一个开源内存数据结构存储,用作数据库,缓存和消息代理,但本文仅演示缓存集成。
|
25天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
59 0
|
2月前
|
缓存 Java 数据库
优化您的Spring应用程序:缓存注解的精要指南
优化您的Spring应用程序:缓存注解的精要指南
45 0
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
45 1
|
1月前
|
Java API Maven
使用Java和Spring Boot构建RESTful API
使用Java和Spring Boot构建RESTful API
16 0
|
2月前
|
存储 缓存 Java
spring的三级缓存,以及循环依赖的形成和解决(详细)
spring的三级缓存,以及循环依赖的形成和解决(详细)
168 0