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

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

前言


缓存(Cache)是计算机领域一个极其重要的概念,它是提高硬件(比如CPU、显卡)、软件运行效率非常重要且有效的一个手段,它的最大特点就一个字:速度非常快。


缓存就是数据交换的缓冲区(称作:Cache),当要读取数据时,会首先从缓存汇总查询数据,有则直接执行返回,速度飞快。它被运用在计算机领域的各个方面,介绍如下:


  • 操作系统磁盘缓存 ——> 减少磁盘机械操作
  • Web服务器缓存——>减少应用服务器请求
  • 客户端浏览器缓存——>减少对网站的访问
  • 应用程序缓存——>减少对数据库的查询
  • 数据库缓存——>减少文件系统IO


本文讲解的缓存就是运用在我们应用程序(软件)上的缓存,并且主要指的是在Spring环境下对缓存的使用。随着Spring框架的普及和渗透,在Spring应用中使用缓存,应该成为了当下Java开发者必备的一个基础技能了~


本文主要讲解Spring对缓存的抽象,当然也会对JSR107缓存抽象进行概念性的介绍。


JSR107缓存抽象:JCache


说起JSR107或者说是JCache,估摸大多数小伙伴都会觉得非常的陌生,没用过且还没听过。


JSR107的草案提得其实是非常的早的,但是第一个Final Release版本却一直难产到了2014年,如图(本文截自JSR官网):


image.png

虽然最终它还是被作为JSR规范提出了,但那时已经4102年了,黄瓜菜早就凉凉~


在还没有缓存规范出来之前,作为Java市场标准制定的强有力竞争者:Spring框架动作频频,早在2011年就提供了它自己的缓存抽象(Spring3.1)。这一切依托于Spring的良好生态下,各大缓存厂商纷纷提供了实现产品。


因此目前而言,关于缓存这块业界有个通识:


  • Spring Cache缓存抽象已经成了业界实际的标准(几乎所有产品都支持)
  • JSR107仅仅只是官方的标准而已(支持的产品并不多)


因为JSR107使用得极少,因此此处对它只做比较简单的一个概念介绍即可。


若要使用JCache,首先我们得额外导包(API包):

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.1</version>
</dependency>


2019年5月发布的最新的1.1.1版本。1.0.0版本是2014年5月发布的


image.png



从这个jar的类里可以看到,它几乎所有都是接口,自己并不提供具体实现(第三方厂商自行实现)。

JCache的实现产品挺少的,Ehcache3.x有实现JSR107相关规范接口

它的核心类的层次结构图:

image.png

  • CachingProvider:创建、配置、获取、管理和控制多个CacheManager
  • CacheManager:创建、配置、获取、管理和控制多个唯一命名的Cache。(一个CacheManager仅被一个CachingProvider所拥有)
  • Cache:一个类似Map的数据结构。(一个Cache仅被一个CacheManager所拥有)
  • Entry:一个存储在Cache中的key-value对
  • Expiry:每一个存储在Cache中的条目有一个定义的有效期,过期后不可访问、更新、删除。缓存有效期可以通过ExpiryPolicy设置


说实话,我个人认为JCache的这个设计太大而全了,导致我们使用它的复杂度是非常高的,因此难以流行起来。(其实JavaEE的很多设计都有这个通病,标准过于复杂,落地实操性很差~)


我看网上有小伙伴评论说:JSR107的设计简直莫名其妙。

其实啊,针对这种评论一定要辩证性的看待,毕竟JSR是全球顶级专家一起制定的,整体优秀性我觉得是毋庸置疑的,只是它作为标准,它不能对那20%的场景避而不谈,而Spring却可以,这就是差别~


Spring缓存抽象


上面说了JCache真正发布都到2014年了,而早在2011年Spring3.1版本就定义了它自己的缓存抽象,旨在帮助开发者简化缓存的开发,并且最终流行开来。


image.png


从截图中可以看到,它被定义在spring-context里面的,作为上下文的核心内容,并不需要额外导包

Spring的缓存抽象相关类的层次结构非常简单:


image.png


  • CacheManager:缓存管理器。管理各种缓存(Cache)组件
  • Cache:为缓存的组件规范定义,包含缓存的各种操作集合。比如它有很多实现:ConcurrentMapCache、RedisCache、EhCacheCache(额外导包)


说明:看到这个层次结构,很多小伙伴会问为何没有Expire的定义?

这里我想说:这也是我比较费解的地方之一。Expire作为缓存非常重要的能力,为何不抽象出来呢???这也是我们经常苦恼的地方:@Cacheable注解竟然不支持TTL过期时间的设置,着实让人很蛋疼~~~


我个人把Spring没有Expire这个理解为Spring缓存抽象的一个功能缺失,说不客气点就是Spring的一个Bug。不知各位对此是否和我相同意见呢?欢迎砸我讨论


至于为何它一直都没有“修复”?我感觉是因为Cache属于它对外公布的API,各大产品都自己实现了Expire,而且方式不尽相同,所以最终它想统一就很难了,很难做到最好的兼容性~


CacheManager和Cache的使用示例


CacheManager简单描述就是用来存放Cache,Cache用于存放具体的key-value值。

比如一个名为"汽车厂"的Cache,那你就可以通过这个名字从CacheManager拿出这个Cache,然后往里面缓存汽车。


首先看看CacheManager这个接口:


// pring's central cache manager SPI.  它是个SPI接口
// @since 3.1
public interface CacheManager {
  @Nullable
  Cache getCache(String name);
  // 管理的所有的Cache的names~
  Collection<String> getCacheNames();
}


它的继承树如下(不进行任何额外导包的情况下):


image.png


image.png


这些实现是Spring内置的最基础的缓存管理器类。

AbstractCacheManager


// @since 3.1 实现了InitializingBean接口~~
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
  // 保存着所有的Cache对象~~key为名字
  private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);  
  //  此处使用了volatile 关键字
  private volatile Set<String> cacheNames = Collections.emptySet();
  @Override
  public void afterPropertiesSet() {
    initializeCaches();
  }
  // @since 4.2.2  模版方法模式。   abstract方法loadCaches()交给子类实现~~~
  public void initializeCaches() {
    Collection<? extends Cache> caches = loadCaches();
    synchronized (this.cacheMap) {
      this.cacheNames = Collections.emptySet();
      this.cacheMap.clear();
      Set<String> cacheNames = new LinkedHashSet<>(caches.size());
      for (Cache cache : caches) {
        String name = cache.getName();
        // decorateCache是protected方法,交给子类  不复写也无所谓~~~
        this.cacheMap.put(name, decorateCache(cache));
        cacheNames.add(name);
      }
      // cacheNames是个只读视图~(框架设计中考虑读写特性~)
      this.cacheNames = Collections.unmodifiableSet(cacheNames);
    }
  }
  protected abstract Collection<? extends Cache> loadCaches();
  // 根据名称 获取Cache对象。若没有,就返回null
  @Override
  @Nullable
  public Cache getCache(String name) {
    Cache cache = this.cacheMap.get(name);
    if (cache != null) {
      return cache;
    } else {
      // Fully synchronize now for missing cache creation...
      synchronized (this.cacheMap) {
        cache = this.cacheMap.get(name);
        if (cache == null) {
          // getMissingCache默认直接返回null,交给子类复写~~~~
          // 将决定权交给实现者,你可以创建一个Cache,或者记录日志
          cache = getMissingCache(name);
          // cache  != null,主要靠getMissingCache这个方法了~~~向一个工厂一样创建一个新的~~~
          if (cache != null) {
            cache = decorateCache(cache);
            this.cacheMap.put(name, cache);
            // 向全局缓存里面再添加进去一个Cache~~~~
            updateCacheNames(name);
          }
        }
        return cache;
      }
    }
  }
  @Override
  public Collection<String> getCacheNames() {
    return this.cacheNames;
  }
  // @since 4.1
  protected final Cache lookupCache(String name) {
    return this.cacheMap.get(name);
  }
  ...
}



这个抽象类其实还蛮重要的,它提供了基本的操作,如果已存的CacheManager们都无法满足你的要求,你可以自己通过继承AbstractCacheManager实现一个自己的CacheManager

比如Redis相关的RedisCacheManager就是继承自它的~

相关文章
|
4月前
|
前端开发 Java API
利用 Spring WebFlux 技术打造高效非阻塞 API 的完整开发方案与实践技巧
本文介绍了如何使用Spring WebFlux构建高效、可扩展的非阻塞API,涵盖响应式编程核心概念、技术方案设计及具体实现示例,适用于高并发场景下的API开发。
391 0
|
3月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
2663 1
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
|
3月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
616 5
|
3月前
|
存储 缓存 Java
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
缓存是提升应用性能的重要技术,Spring框架提供了丰富的缓存注解,如`@Cacheable`、`@CacheEvict`等,帮助开发者简化缓存管理。本文介绍了如何在Spring中配置缓存管理器,使用缓存注解优化数据访问,并探讨了缓存的最佳实践,以提升系统响应速度与可扩展性。
333 0
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
|
4月前
|
缓存 Java API
Spring WebFlux 2025 实操指南详解高性能非阻塞 API 开发全流程核心技巧
本指南基于Spring WebFlux 2025最新技术栈,详解如何构建高性能非阻塞API。涵盖环境搭建、响应式数据访问、注解与函数式两种API开发模式、响应式客户端使用、测试方法及性能优化技巧,助你掌握Spring WebFlux全流程开发核心实践。
777 0
|
6月前
|
Java API 网络架构
基于 Spring Boot 框架开发 REST API 接口实践指南
本文详解基于Spring Boot 3.x构建REST API的完整开发流程,涵盖环境搭建、领域建模、响应式编程、安全控制、容器化部署及性能优化等关键环节,助力开发者打造高效稳定的后端服务。
905 1
|
7月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
250 32
|
8月前
|
存储 人工智能 Java
Spring AI与DeepSeek实战四:系统API调用
在AI应用开发中,工具调用是增强大模型能力的核心技术,通过让模型与外部API或工具交互,可实现实时信息检索(如天气查询、新闻获取)、系统操作(如创建任务、发送邮件)等功能;本文结合Spring AI与大模型,演示如何通过Tool Calling实现系统API调用,同时处理多轮对话中的会话记忆。
1548 57
|
5月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
469 0
|
12月前
|
存储 安全 Java
Spring Boot 编写 API 的 10条最佳实践
本文总结了 10 个编写 Spring Boot API 的最佳实践,包括 RESTful API 设计原则、注解使用、依赖注入、异常处理、数据传输对象(DTO)建模、安全措施、版本控制、文档生成、测试策略以及监控和日志记录。每个实践都配有详细的编码示例和解释,帮助开发者像专业人士一样构建高质量的 API。
396 9

热门文章

最新文章