• Spring Cache缓存原理与Redis实践


    说到Spring Boot缓存,那就不得不提JSR-107规范,它告诉我们在Java中如何规范地使用缓存。

    JSR是Java Specification Requests的简称,通常译为”Java 规范提案“。具体而言,是指向JCP(Java Community Process,Java标准制定组织)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,通过一定的标准测试后,就可以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

    JSR-107规范即JCache API,JCache规范定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCache的缓存数据,可以加快大多数查询的反应时间(内部测试表明反应时间大约快15倍)。

    一、JCache

    在Spring Boot中使用缓存之前,我们有必要了解一下JCache。JCache定义了五个核心接口,分别是CachingProvider,CacheManager,Cache,Entry和Expiry。

    • 一个CachingProvider可以创建和管理多个CacheManager,并且一个CacheManager只能被一个CachingProvider所拥有,而一个应用可以在运行期间访问多个CachingProvider。

    • 一个CacheManager可以创建和管理多个唯一命名的Cache,且一个Cache只能被一个CacheManager所拥有,这些Cache存在于CacheManager的上下文中。

    • Cache是一个类似Map的数据结构

    • Entry是一个存储在Cache中的key-value对

    • Expiry是指存储在Cache中的Entry的有效期,一旦超过这个时间,Entry将处于过期状态,即不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

      image

     

    二、Spring Cache原理

    Spring 3.1开始,引入了Spring Cache,即Spring 缓存抽象。通过定义org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术,并支持使用JCache注解简化开发过程。

    Cache接口为缓存的组件规范定义,包含缓存的各种操作集合。Spring中为Cache接口提供了各种xxxCache的实现:RedisCache,EhCacheCache,ConcurrentMapCache等。
    我们通过部分源码详细了解一下Cache接口和CacheManager接口。

    Cache接口

    1. public interface Cache {
    2.     //Cache名称
    3.     String getName();
    4.     //Cache负责缓存的对象
    5.     Object getNativeCache();
    6.     /**
    7.      * 获取key对应的ValueWrapper
    8.      * 没有对应的key,则返回null
    9.      * key对应的value是null,则返回null对应的ValueWrapper
    10.      */
    11.     @Nullable
    12.     Cache.ValueWrapper get(Object key);
    13.     //返回key对应type类型的value
    14.     @Nullable
    15.      T get(Object key, @Nullable Class type);
    16.     //返回key对应的value,没有则缓存Callable::call,并返回
    17.     @Nullable
    18.      T get(Object key, Callable valueLoader);
    19.     //缓存目标key-value(替换旧值),不保证实时性
    20.     void put(Object key, @Nullable Object value);
    21.     //插入缓存,并返回该key对应的value;先调用get,不存在则用put实现
    22.     @Nullable
    23.     default Cache.ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
    24.         Cache.ValueWrapper existingValue = this.get(key);
    25.         if (existingValue == null) {
    26.             this.put(key, value);
    27.         }
    28.         return existingValue;
    29.     }
    30.     //删除缓存,不保证实时性
    31.     void evict(Object key);
    32.     //立即删除缓存:返回false表示剔除前不存在制定key活不确定是否存在;返回true,表示该key之前存在
    33.     default boolean evictIfPresent(Object key) {
    34.         this.evict(key);
    35.         return false;
    36.     }
    37.     //清除所有缓存,不保证实时性
    38.     void clear();
    39.     //立即清楚所有缓存,返回false表示清除前没有缓存或不能确定是否有;返回true表示清除前有缓存
    40.     default boolean invalidate() {
    41.         this.clear();
    42.         return false;
    43.     }
    44.     public static class ValueRetrievalException extends RuntimeException {
    45.         @Nullable
    46.         private final Object key;
    47.         public ValueRetrievalException(@Nullable Object key, Callable loader, Throwable ex) {
    48.             super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
    49.             this.key = key;
    50.         }
    51.         @Nullable
    52.         public Object getKey() {
    53.             return this.key;
    54.         }
    55.     }
    56.     //缓存值的一个包装器接口,实现类为SimpleValueWrapper
    57.     @FunctionalInterface
    58.     public interface ValueWrapper {
    59.         @Nullable
    60.         Object get();
    61.     }
    62. }

    可以看出,Cache接口抽象了缓存的 get put evict 等相关操作。

    AbstractValueAdaptingCache

    1. public abstract class AbstractValueAdaptingCache implements Cache {
    2.     //是否允许Null值
    3.     private final boolean allowNullValues;
    4.     protected AbstractValueAdaptingCache(boolean allowNullValues) {
    5.         this.allowNullValues = allowNullValues;
    6.     }
    7.     public final boolean isAllowNullValues() {
    8.         return this.allowNullValues;
    9.     }
    10.     @Nullable
    11.     public ValueWrapper get(Object key) {
    12.         return this.toValueWrapper(this.lookup(key));
    13.     }
    14.     @Nullable
    15.     public  T get(Object key, @Nullable Class type) {
    16.         //查询到的缓存值做fromStoreValue转换
    17.         Object value = this.fromStoreValue(this.lookup(key));
    18.         //转换后非null值且无法转换为type类型则抛出异常
    19.         if (value != null && type != null && !type.isInstance(value)) {
    20.             throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
    21.         } else {
    22.             return value;
    23.         }
    24.     }
    25.     //从缓存中获取key对应的value,子类实现
    26.     @Nullable
    27.     protected abstract Object lookup(Object key);
    28.     //对于从缓存中获取的值,允许为空且值为NullValue时,处理为null
    29.     @Nullable
    30.     protected Object fromStoreValue(@Nullable Object storeValue) {
    31.         return this.allowNullValues && storeValue == NullValue.INSTANCE ? null : storeValue;
    32.     }
    33.     //对于要插入缓存的null值,在允许null值的情况下处理为NullValue;否则抛出异常IllegalArgumentException
    34.     protected Object toStoreValue(@Nullable Object userValue) {
    35.         if (userValue == null) {
    36.             if (this.allowNullValues) {
    37.                 return NullValue.INSTANCE;
    38.             } else {
    39.                 throw new IllegalArgumentException("Cache '" + this.getName() + "' is configured to not allow null values but null was provided");
    40.             }
    41.         } else {
    42.             return userValue;
    43.         }
    44.     }
    45.     //get操作依据,查询到缓存值非null,则fromStoreValue转换后包装成SimpleValueWrapper返回
    46.     @Nullable
    47.     protected ValueWrapper toValueWrapper(@Nullable Object storeValue) {
    48.         return storeValue != null ? new SimpleValueWrapper(this.fromStoreValue(storeValue)) : null;
    49.     }
    50. }

    抽象类AbstractValueAdaptingCache实现了Cache接口,主要抽象了对NULL值的处理逻辑。

    • allowNullValues属性表示是否允许处理NULL值的缓存

    • fromStoreValue方法处理NULL值的get操作,在属性allowNullValues为true的情况下,将NullValue处理为NULL

    • toStoreValue方法处理NULL值得put操作,在属性allowNullValues为true的情况下,将NULL处理为NullValue,否则抛出异常

    • toValueWrapper方法提供Cache接口中get方法的默认实现,从缓存中读取值,再通过fromStoreValue转化,最后包装为SimpleValueWrapper返回

    • ValueWrapper get(Object key)T get(Object key, @Nullable Classtype)方法基于上述方法实现

    • ValueWrapper get(Object key)@Nullable Classtype)方法基于上述方法实现

    • lookup抽象方法用于给子类获取真正的缓存值

    ConcurrentMapCache

    1. public class ConcurrentMapCache extends AbstractValueAdaptingCache {
    2.     private final String name;
    3.     //定义ConcurrentMap缓存
    4.     private final ConcurrentMap store;
    5.     //如果要缓存的是值对象的copy,则由此序列化代理类处理
    6.     @Nullable
    7.     private final SerializationDelegate serialization;
    8.     public ConcurrentMapCache(String name) {
    9.         this(name, new ConcurrentHashMap(256), true);
    10.     }
    11.     //默认允许处理null
    12.     public ConcurrentMapCache(String name, boolean allowNullValues) {
    13.         this(name, new ConcurrentHashMap(256), allowNullValues);
    14.     }
    15.     //默认serialization = null
    16.     public ConcurrentMapCache(String name, ConcurrentMap store, boolean allowNullValues) {
    17.         this(name, store, allowNullValues, (SerializationDelegate)null);
    18.     }
    19.     protected ConcurrentMapCache(String name, ConcurrentMap store, boolean allowNullValues, @Nullable SerializationDelegate serialization) {
    20.         super(allowNullValues);
    21.         Assert.notNull(name, "Name must not be null");
    22.         Assert.notNull(store, "Store must not be null");
    23.         this.name = name;
    24.         this.store = store;
    25.         this.serialization = serialization;
    26.     }
    27.     //serialization不为空,缓存值对象的copy
    28.     public final boolean isStoreByValue() {
    29.         return this.serialization != null;
    30.     }
    31.     public final String getName() {
    32.         return this.name;
    33.     }
    34.     public final ConcurrentMap getNativeCache() {
    35.         return this.store;
    36.     }
    37.     //实现lookup:store#get
    38.     @Nullable
    39.     protected Object lookup(Object key) {
    40.         return this.store.get(key);
    41.     }
    42.     //基于ConcurrentMap::computeIfAbsent方法实现;get和put的值由fromStoreValue和toStoreValue处理Null
    43.     @Nullable
    44.     public  T get(Object key, Callable valueLoader) {
    45.         return this.fromStoreValue(this.store.computeIfAbsent(key, (k) -> {
    46.             try {
    47.                 return this.toStoreValue(valueLoader.call());
    48.             } catch (Throwable var5) {
    49.                 throw new ValueRetrievalException(key, valueLoader, var5);
    50.             }
    51.         }));
    52.     }
    53.     public void put(Object key, @Nullable Object value) {
    54.         this.store.put(key, this.toStoreValue(value));
    55.     }
    56.     @Nullable
    57.     public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
    58.         Object existing = this.store.putIfAbsent(key, this.toStoreValue(value));
    59.         return this.toValueWrapper(existing);
    60.     }
    61.     public void evict(Object key) {
    62.         this.store.remove(key);
    63.     }
    64.     public boolean evictIfPresent(Object key) {
    65.         return this.store.remove(key) != null;
    66.     }
    67.     public void clear() {
    68.         this.store.clear();
    69.     }
    70.     public boolean invalidate() {
    71.         boolean notEmpty = !this.store.isEmpty();
    72.         this.store.clear();
    73.         return notEmpty;
    74.     }
    75.     protected Object toStoreValue(@Nullable Object userValue) {
    76.         Object storeValue = super.toStoreValue(userValue);
    77.         if (this.serialization != null) {
    78.             try {
    79.                 return this.serialization.serializeToByteArray(storeValue);
    80.             } catch (Throwable var4) {
    81.                 throw new IllegalArgumentException("Failed to serialize cache value '" + userValue + "'. Does it implement Serializable?", var4);
    82.             }
    83.         } else {
    84.             return storeValue;
    85.         }
    86.     }
    87.     protected Object fromStoreValue(@Nullable Object storeValue) {
    88.         if (storeValue != null && this.serialization != null) {
    89.             try {
    90.                 return super.fromStoreValue(this.serialization.deserializeFromByteArray((byte[])((byte[])storeValue)));
    91.             } catch (Throwable var3) {
    92.                 throw new IllegalArgumentException("Failed to deserialize cache value '" + storeValue + "'", var3);
    93.             }
    94.         } else {
    95.             return super.fromStoreValue(storeValue);
    96.         }
    97.     }
    98. }

    ConcurrentMapCache继承了抽象类AbstractValueAdaptingCache,是Spring的默认缓存实现。它支持对缓存对象copy的缓存,由SerializationDelegate serialization 处理序列化,默认为 null 即基于引用的缓存。缓存相关操作基于基类 AbstractValueAdaptingCache 的 null 值处理,默认允许为 null。

    CacheManager

    1. public interface CacheManager {
    2.     @Nullable
    3.     //获取指定name的Cache,可能延迟创建
    4.     Cache getCache(String name);
    5.     //获取当前CacheManager下的Cache name集合
    6.     Collection getCacheNames();
    7. }

    CacheManager 基于 name 管理一组 Cache。当然,CacheManager也有很多实现类,如ConcurrentMapCacheManager、AbstractCacheManager及SimpleCacheManager,这些xxxCacheManager类都是为了制定Cache的管理规则,这里就不再深入探讨了。

    三、Spring Cache实践

    除了第二章中提到的Cache接口和CacheManager接口,在使用Spring 缓存抽象时,我们还会用到一些JCache注解。

     

     

  • 相关阅读:
    一片编程文章
    深度学习图像分割标签onehot
    A股风格因子看板 (2023.09 第03期)
    java计算机毕业设计教务管理系统源码+数据库+lw文档+系统
    Nacos做配置中心使用
    【计算机视觉40例】案例25:风格迁移
    【Redis使用】一年多来redis使用笔记md文档,第(2)篇:命令和数据库操作
    低代码在企业数字化转型中有什么价值?
    No converter found for return value of type: class
    力扣26--删除有序数组中的重复项
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/126225712