惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
量子位
小众软件
小众软件
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
T
Threat Research - Cisco Blogs
Latest news
Latest news
Spread Privacy
Spread Privacy
C
Cyber Attacks, Cyber Crime and Cyber Security
NISL@THU
NISL@THU
T
Tor Project blog
Hacker News: Ask HN
Hacker News: Ask HN
V2EX - 技术
V2EX - 技术
T
The Exploit Database - CXSecurity.com
博客园 - 三生石上(FineUI控件)
K
Kaspersky official blog
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗
博客园 - 聂微东
Last Week in AI
Last Week in AI
爱范儿
爱范儿
腾讯CDC
博客园 - Franky
美团技术团队
J
Java Code Geeks
O
OpenAI News
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
有赞技术团队
有赞技术团队
T
Threatpost
G
GRAHAM CLULEY
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
I
Intezer
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Blog of Author Tim Ferriss
S
Security @ Cisco Blogs
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
Stack Overflow Blog
Stack Overflow Blog
Scott Helme
Scott Helme
H
Hacker News: Front Page
Cloudbric
Cloudbric

XINDOO

关于内卷,几个值得深想的洞察 当创作被 Skill 化:我用 AI 写了一部 320 章的长篇网文 AI第一剑,先斩程序员 AI 也会偷懒?这个 PUA 工具专治“摸鱼式编程” Gstack 深度解析:YC CEO 开源的 AI 工程团队 GitHub Trending霸榜!深度解析AI Coding辅助神器 Superpowers 深度探讨:从 OpenClaw 爆火,看 AI Agent 的真相与程序员的未来 我复刻了NotebookLM的信息图功能 我用AI写了部小说,这里是整个过程 [翻译]我在谷歌14年学到的21堂课 2万字吊打40万字:为什么我的“牛马Agent”比“数字分身”更聪明? 最近AI领域爆火的 Agent Skills 是什么? 从计算机科学的视角来看拖延症 一周改6个库后我悟了:AI时代,程序员正在退化成“甲方” 使用n8n做一个自动同步更新的Github项目问答机器人 Agent设计模式——第 20 章:优先级排序 Agent设计模式——第 1 章:提示词链 Agent设计模式——附录 D - 使用 AgentSpace 构建 Agent Agent设计模式——第 19 章:评估和监控 Agent设计模式——第 10 章:模型上下文协议 (MCP) Agent设计模式——第 21 章:探索和发现 Agent设计模式——智能体设计模式 Agent设计模式——第 11 章:目标设定和监控 Agent设计模式——第 9 章:学习和适应 Agent设计模式——第 16 章:资源感知优化 Agent设计模式——附录 G - 编码 Agent Agent设计模式——第 13 章:人机协同 Agent设计模式——第 17 章:推理技术 Agent设计模式——附录 F - 深入剖析:Agent 推理引擎的内部运作机制 Agent设计模式——章节目录 Agent设计模式—— Agent设计模式——第 7 章:多 Agent 协作 Agent设计模式——附录 E - 命令行界面中的 AI Agent Agent设计模式——附录 C - Agentic 框架快速概览 Agent设计模式——第 3 章:并行化 Agent设计模式——**常见问题解答:Agentic 设计模式** Agent设计模式——第 14 章:知识检索(RAG) Agent设计模式——第 18 章:Guardrails/安全模式 Agent设计模式——第 15 章:Agent 间通信(A2A) Agent设计模式——第 8 章:内存管理 Agent设计模式——第 12 章:异常处理和恢复 Agent设计模式——第 4 章:反思 Agent设计模式——附录 B - AI Agentic 交互:从图形界面到现实世界环境 Agent设计模式——第 5 章:工具使用(函数调用) Agent设计模式——结论 Agent设计模式——第 6 章:规划 Agent设计模式——第 2 章:路由 从经验主义到贝叶斯理论:如何排查线上问题 我用AI为自己造了一把安全的开发者“瑞士军刀”” 从LLM和MCP的协同过程看如何做优化 打通Dify与AI工具生态:将Workflow转为MCP工具的实践 一文了解知识库背后的技术RAG AI应用的五个级别:从入门到专家的进阶之路 一文入门AI圈最近爆火的MCP协议 HTTP/3:性能改进(第 2 部分) deepseek-r1祛魅:从过度热捧到理性认知⁠ 为什么AI智能体需要工作流 如何用GPT-4o解读视频 json命令行处理神器jq介绍 OpenAI的结构化浅析 从大模型的原理到提示词优化 从经济学原理看团队分工合作 [翻译]关于人工智能的30个思考 从马斯洛需求层次理论谈职场激励 知识与智慧 如何使用大语言模型绘制专业图表 两个开源项目打造自己的大模型聚合平台 我让gpt4o给我推荐了一千多次书 得到了这些数据 用Langchain创建一个可以总结网页内容的Agent 推荐一个好用的命令行工具ShellGPT 关于ffmpeg height not divisible by 2的错误 使用Certbot解决https证书自动更新的问题 软件开发中的抓大放小vs极致细节思维 OpenAI Assistants-API简明教程 OpenAI的多函数调用(Multiple Function Calling)简介 如何使用ffmpeg制作透明背景的视频 spring-kafka中ContainerProperties.AckMode详解 如何在地图上寻找最密集点的位置? IO密集型服务提升性能的三种方法 职场中的基本归因错误和自利归因 使用javax.validation.constraints校验参数合法性 Java Optional:让你的代码更优雅 ChatGPT函数调用初体验:让ChatGPT具备抓取网页文本的能力 如何使用ChatGPT提升自己的“码”力? 使用ffmpeg拼接两张图片 ThreadPoolExecutor——高效处理并发任务的必备良器 从CPU的视角看 多线程代码为什么那么难写! 使用ffmpeg缩小视频体积的几种方式 Linux parallel 命令使用手册 为什么说过早优化是万恶之源? Linux xargs命令介绍 深入理解Spring的事件通知机制 Java高并发之CyclicBarrier简介 聊一聊过度设计! 详解Redisson分布式限流的实现原理 Java中使用HashMap时指定初始化容量性能一定会更好吗? 如何用ffmpeg截取视频片段&截取时间不准确的坑 XINDOO的2022年年终总结 使用ffmpeg将视频转成HLS(m3u8)格式 谷歌Guava LoadingCache介绍
Spring Cache简明教程
xindoo · 2024-03-17 · via XINDOO

@[TOC](Spring Cache简明教程)

引言

  在我上一篇文章如何正确使用缓存来提升系统性能中,我从偏理论的视角介绍了Cache在性能优化中的必要性,在这篇文章中我们介绍Spring全家桶中和cache相关Spring-Cache。

什么是Spring Cache?

  Spring Cache是Spring框架提供的一个抽象层,专注于提供一种透明的方式来添加缓存功能到Spring应用程序中。它不是一个具体的缓存实现,而是提供了一套创建和管理缓存的标准,并能够与多种缓存实现无缝集成,例如Ehcache、Caffeine、Redisson等。

核心特性

  以下是Spring Cache的一些核心特性:

  • 声明式缓存抽象:通过Java注解,开发者可以声明性地控制方法的缓存行为,而无需编写具体的缓存逻辑。
  • 无需改变代码结构:缓存逻辑通过AOP增强被注解的方法,因此不需要修改方法的实际代码。
  • 支持多种缓存库:与多个流行的缓存库兼容,开发者可以根据自身需求选择最适合的缓存解决方案。
  • 灵活的缓存配置:可以通过配置文件灵活地管理缓存行为,包括缓存的名称、过期时间和条件等。
  • 动态缓存决策:支持在运行时根据方法执行的上下文动态地做出缓存决策。

应用场景

  Spring Cache适用于以下应用场景:

  • 提高性能:对于那些计算成本高昂或者频繁访问的数据,通过缓存可以显著提高系统的响应速度。
  • 减少数据库压力:缓存可以减少数据库的读操作,对于读多写少的场景特别有用。
  • 提高系统可扩展性:通过使用分布式缓存,可以在不增加数据库负荷的情况下,横向扩展应用程序。

如何工作

  Spring Cache背后的工作原理基于Spring AOP(面向切面编程),它会在运行时动态地创建代理对象,来拦截对被注解方法的调用。根据注解的不同,Spring Cache可以执行如下操作:

  • @Cacheable:在方法执行前先检查缓存,如果缓存中已经存在相应的数据,则直接返回缓存数据而不执行方法。
  • @CachePut:无论如何都会执行方法,并将执行结果放入指定的缓存中。
  • @CacheEvict:删除缓存中的数据,通常用于删除操作或数据更新后的缓存同步。
  • @Caching:组合多个缓存操作,可以同时使用以上几种注解。

通过上述机制,Spring Cache提供了一个简单而强大的缓存管理能力,使得开发者能够专注于业务逻辑的实现,而将缓存的维护交给框架去处理。

如何使用

1. 添加依赖

  我们拿SpringBoot Maven的项目为例,说下如何在项目中使用Spring Cache,首先很简单,需要在pom文件中引入Spring Cache相关的依赖。

<dependencies>
    <!-- 添加Spring Boot Cache Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- 如果使用特定的缓存实现,如Caffeine -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
</dependencies>

2. 启用缓存

  另外还需要在Spring Boot应用程序的主类或任何配置类上使用@EnableCaching注解来启用缓存支持。

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableCaching
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

3. 配置缓存

  虽然Spring Boot为许多缓存实现提供了自动配置,但你也可以通过application.properties或application.yml文件进行自定义配置。例如,如果你使用Caffeine作为缓存实现,可以按以下方式配置:

# application.properties
spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

当然你也可以通过代码的形式定义CacheManager,实现对Cache的配置,代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("cache1", "cache2");
        cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES));
        return cacheManager;
    }
}

4. 使用缓存注解

  在服务中,你可以通过在方法上添加相应的缓存注解来实现缓存逻辑。

  • 使用@Cacheable来缓存方法的返回结果。
import org.springframework.cache.annotation.Cacheable;

public class SomeService {

    @Cacheable("cache1")
    public SomeObject getSomeObject(String id) {
        // 方法实现,如果缓存中有对应id的对象,则不执行此代码
        return fetchFromDatabase(id);
    }
}
  • 使用@CachePut来更新缓存。
import org.springframework.cache.annotation.CachePut;

public class SomeService {

    @CachePut(value = "cache1", key = "#someObject.id")
    public SomeObject updateSomeObject(SomeObject someObject) {
        // 方法实现,总是执行并刷新缓存
        return saveToDatabase(someObject);
    }
}
  • 使用@CacheEvict来清除缓存。
import org.springframework.cache.annotation.CacheEvict;

public class SomeService {

    @CacheEvict(value = "cache1", key = "#id")
    public void deleteSomeObject(String id) {
        // 方法实现,删除对象的同时清除缓存
        removeFromDatabase(id);
    }
}

你还可以使用@Caching来组合多个缓存操作。

使用缓存的注意事项

  使用Spring Cache时,需要注意以下几个关键点:

缓存的数据序列化

  当使用分布式缓存或需要将缓存数据存储在磁盘上时,数据序列化变得非常重要。你需要确保你的对象可以被序列化和反序列化,否则会抛出异常。对于复杂对象,考虑使用JSON或其他自定义序列化策略,当你不指定序列化策略时,默认会使用java序列化,这时候就要求你必须实现Serializable接口。

缓存键的生成

  默认情况下,Spring Cache使用方法参数的hashCode()equals()方法来生成缓存键。如果你的方法参数是自定义的对象,确保这些方法被适当地覆盖。你也可以通过实现KeyGenerator接口或使用key属性自定义键的生成。

缓存内容的一致性

  缓存数据可能会与数据库中的数据不一致。当数据被更新或删除时,你需要使用@CachePut@CacheEvict注解来确保缓存与数据源保持同步。

缓存的并发问题

  虽然缓存操作通常是原子性的,但在高并发环境下仍然可能遇到并发问题。例如,多个线程可能同时计算同一个缓存缺失的值。为了避免这种情况,你可能需要使用锁或其他同步机制。

缓存穿透

  缓存穿透是指查询不存在的数据。因为缓存不会存储这样的数据,所以每次查询都会打到后端数据库,从而可能造成数据库的压力。为了预防这种情况,可以采用布隆过滤器或者将查询结果为空的情况也缓存起来。

缓存雪崩

  缓存雪崩指在缓存失效后,大量的请求同时到达数据库,可能会导致数据库瞬时压力过大。为了防止这种情况,可以设置不同的缓存过期时间,使用缓存预热策略,或者实施熔断限流措施。

缓存的存储容量

  对于本地缓存,缓存的大小应当根据可用内存合理配置,避免内存溢出。对于分布式缓存,应当考虑其存储容量和扩展性。

方法的可见性和返回类型

  @Cacheable本身逻辑也是基于SpringAOP实现的,所以需要和其他缓存注解一样应用于公共方法。对私有方法、final方法或类、static方法使用缓存注解是无效的,因为Spring的AOP无法拦截这些方法的调用。同样,缓存方法的返回类型应该是非null值,因为大多数缓存实现都不会存储null值。如果方法可能返回null,那么需要进行额外的处理来避免缓存穿透。

事务性操作和缓存

  如果在事务性操作中使用缓存,需要注意事务的传播行为和缓存操作的顺序。错误的操作顺序可能会导致缓存与数据库状态不一致。

总结

  本文详细介绍了Spring Cache的使用和注意事项。Spring Cache作为Spring框架提供的缓存抽象,允许通过声明式注解轻松地在应用中集成缓存,以此提升性能和减少开发时间。以下是本文关键点的总结:

  1. Spring Cache不是缓存实现:它提供了一组与缓存实现无关的接口和注解。
  2. 简单的集成步骤:包括添加依赖、启用缓存、配置缓存以及在方法上使用缓存注解。
  3. 缓存注解的使用:介绍了@Cacheable@CachePut@CacheEvict等注解的使用场景。
  4. 注意事项
    • 数据序列化:确保对象可以被序列化和反序列化。
    • 缓存键生成:覆盖hashCode()equals()或自定义键的生成。
    • 缓存内容一致性:使用注解确保缓存与数据源同步。
    • 并发问题:可能需要锁或其他同步机制。
    • 缓存穿透:使用布隆过滤器或缓存空查询。
    • 缓存雪崩:设置不同的缓存过期时间,缓存预热策略,或实施熔断限流。
    • 缓存容量:合理配置本地缓存大小,考虑分布式缓存的存储容量和扩展性。
    • 方法的可见性:缓存注解应用于公共方法。
    • 事务性操作:注意事务的传播行为和缓存操作的顺序。

备注:本文使用了部分AI生成内容,作者已对有效性做了校验,请放心查阅。