it编程 > 编程语言 > Java

Springboot 缓存@Cacheable 的引入和使用

0人参与 2026-03-19 Java

前言

一、@cacheable 是什么?

@cacheable 是 spring 缓存抽象的核心注解,作用是将方法的返回结果缓存起来:

二、springboot 项目中如何使用

2.1 基于内存实现:

2.1.1 cacheable 引入并使用:

(1) 引入缓存依赖
在 pom.xml(maven)中添加 spring 缓存启动器(gradle 同理):

<!-- spring 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>

(2)开启缓存功能
在 springboot 启动类上添加 @enablecaching 注解,全局开启缓存支持:

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
import org.springframework.cache.annotation.enablecaching;
@springbootapplication
@enablecaching // 核心:开启缓存
public class cachedemoapplication {
    public static void main(string[] args) {
        springapplication.run(cachedemoapplication.class, args);
    }
}

(3)在查询方法上添加 @cacheable
在 service/controller 的查询方法上标注 @cacheable,指定缓存名称(必填):

import org.springframework.cache.annotation.cacheable;
import org.springframework.stereotype.service;
@service
public class userservice {
    // cachenames:缓存名称(必填,相当于缓存的“命名空间”)
    @cacheable(cachenames = "usercache")
    public user getuserbyid(long id) {
        // 模拟数据库查询(首次调用执行,后续从缓存获取)
        system.out.println("执行数据库查询,用户id:" + id);
        return new user(id, "张三", 20);
    }
    // 模拟用户实体类(需有 getter/setter,lombok 可简化)
    public static class user {
        private long id;
        private string name;
        private integer age;
        public user(long id, string name, integer age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
        // getter/setter 省略(实际开发用 @data 注解)
        public long getid() { return id; }
        public string getname() { return name; }
        public integer getage() { return age; }
    }
}

(4) 更新缓存:

 // 修改用户后更新缓存(key 需与 @cacheable 一致)
 @cacheput(cachenames = "usercache", key = "#user.studentid")
 public studentdto updateuser(studentdto user) {
     system.out.println("更新用户:" + user.getstudentid());
     return user;
 }

(5) 删除缓存:

// 删除用户后清理缓存
@cacheevict(cachenames = "usercache", key = "#id")
public void deleteuser(string id) {
    system.out.println("删除用户:" + id);
}
// 清空整个 usercache 缓存
@cacheevict(cachenames = "usercache", allentries = true)
public void clearusercache() {
    system.out.println("清空用户缓存");
}

2.1.2 cacheable 配置参数

spring:
  cache:
    type: caffeine  # 指定缓存类型为 caffeine(替代默认的 concurrenthashmap/redis)
    caffeine:
      spec: initialcapacity=100,maximumsize=1000,expireafterwrite=10s # caffeine 核心参数

2.2 cacheable 基于redis 缓存

springboot 默认使用「内存缓存(concurrenthashmap)」,重启后缓存丢失,此时可以切换为 redis:

(1) 引入依赖:

<!-- redis jar-->
		<dependency>
			<groupid>org.apache.commons</groupid>
			<artifactid>commons-pool2</artifactid>
		</dependency>
		<dependency>
			<groupid>org.springframework.boot</groupid>
			<artifactid>spring-boot-starter-data-redis</artifactid>
		</dependency>
		<dependency>
			<groupid>com.fasterxml.jackson.core</groupid>
			<artifactid>jackson-databind</artifactid>
			<version>2.13.5</version>
		</dependency>

(2)配置项:

spring:
  cache:
    type: redis
    redis:
      # 1. 全局过期时间(核心,替代 caffeine 的 expireafterwrite)
      time-to-live: 1000s   # 所有缓存默认 10 秒过期(支持 s/m/h/d)
      # 3. 是否缓存空值(防止缓存穿透)
      cache-null-values: true
      # 4. 缓存 key 前缀(避免不同业务 key 冲突)
      key-prefix: "cache:"
      # 5. 是否使用前缀(建议开启)
      use-key-prefix: true
  data:
    redis:
      host: localhost
      port: 6379
      timeout: 120000
      database: 1
    lettuce:
      pool:
        max-active: 50
        max-idle: 8
        max-wait: -1
        min-idle: 1

(3) 测试结果:

 @override
// 使用缓存
 @cacheable(cachenames ="usercache", key = "#root.args[0]")
 public studentdto getstudentbyid(string studentid) {
     log.info("getstudentbyid:{}", studentid);
     studentdto dto = studentdto.builder().studentid(studentid).studentage(12)
             .studentname("test").studentsex("man").build();
     return dto;
 }

redids 缓存结果

2.3 @cacheable 注解属性

(1) 注解属性:

@target({elementtype.method, elementtype.type})
@retention(retentionpolicy.runtime)
@inherited
@documented
public @interface cacheable {
    // 核心属性(按使用频率排序)
    string[] value() default {};          // 等价于 cachenames,缓存名称(必填)
    string[] cachenames() default {};     // 缓存名称(必填,和 value 二选一,推荐用这个)
    string key() default "";              // 自定义缓存键(spel 表达式)
    string keygenerator() default "";     // 自定义键生成器(与 key 互斥)
    string cachemanager() default "";     // 指定缓存管理器(多缓存源时用)
    string cacheresolver() default "";    // 自定义缓存解析器(比 cachemanager 更灵活)
    string condition() default "";        // 缓存条件(满足则缓存,spel)
    string unless() default "";           // 排除条件(满足则不缓存,spel)
    boolean sync() default false;         // 是否同步缓存(解决缓存击穿)
}

(2) redis key 生成策略:

spel 表达式含义示例
#参数名引用方法参数key = “#id”
#参数.属性引用参数对象的属性key = “#user.id”
#result引用方法返回值(仅 condition/unless 生效)key = “#result.id”(不推荐)
#root根对象,包含方法 / 参数等信息key = “#root.methodname + #id”

代码示例:

// 仅以 id 为键(忽略其他参数)
@cacheable(cachenames = "usercache", key = "#id")
public user getuserbyid(long id, string unusedparam) { ... }
// 以参数对象的属性为键
@cacheable(cachenames = "usercache", key = "#user.id + '_' + #user.name")
public user getuserbyuser(user user) { ... }
// 结合根对象:方法名 + 参数(避免不同方法的相同参数冲突)
@cacheable(cachenames = "usercache", key = "#root.methodname + '_' + #id")
public user getuserbyid(long id) { ... }

(3) 条件控制属性
condition(缓存条件,spel 表达式) 作用:仅当表达式结果为 true 时,才将方法结果存入缓存; 执行时机:方法执行后、存入缓存前

// 仅当 id > 0 时缓存(参数条件)
@cacheable(cachenames = "usercache", key = "#id", condition = "#id > 0")
public user getuserbyid(long id) { ... }
// 仅当返回值 age > 18 时缓存(返回值条件)
@cacheable(cachenames = "usercache", key = "#id", condition = "#result.age > 18")
public user getuserbyid(long id) { ... }
// 多条件组合(参数 id > 0 且返回值不为 null)
@cacheable(cachenames = "usercache", key = "#id", condition = "#id > 0 and #result != null")
public user getuserbyid(long id) { ... }

unless(排除缓存条件,spel 表达式)作用:与 condition 相反,当表达式结果为 true 时,不 缓存结果;执行时机:方法执行后、存入缓存前;核心场景:排除 null 值(避免缓存穿透)、排除异常结果等;

// 不缓存 null 值(解决缓存穿透)
@cacheable(cachenames = "usercache", key = "#id", unless = "#result == null")
public user getuserbyid(long id) {
    if (id == 999l) return null; // 不缓存
    return new user(id, "张三", 20); // 缓存
}
// 不缓存 age <= 18 的用户
@cacheable(cachenames = "usercache", key = "#id", unless = "#result.age <= 18")
public user getuserbyid(long id) { ... }

(4)sync(同步缓存,解决缓存击穿)
作用:开启同步模式,当缓存未命中时,多个并发请求只会有一个请求执行方法体,其余请求等待缓存生成(解决热点 key 缓存击穿);默认值:false(非同步,多个请求同时执行方法体);

// 开启同步缓存,解决缓存击穿
@cacheable(cachenames = "usercache", key = "#id", sync = true)
public user getuserbyid(long id) { ... }

注意:
① sync=true 时,unless 属性失效(无法基于返回值排除缓存);
② 仅适用于本地缓存(caffeine),分布式缓存(redis)需结合分布式锁使用

总结

本文记录springboot 缓存@cacheable 的引入和使用。

到此这篇关于springboot 缓存@cacheable 的引入和使用的文章就介绍到这了,更多相关springboot 缓存@cacheable 内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

Spring Boot集成Redis Stream消息队列从入门到实战指南

03-19

Java 并发编程基础概念与常见问题整理

03-19

RabbitMQ  @RabbitListener 与 @RabbitHandler 的使用区别解析

03-19

Java线程死锁的问题解决

03-19

Java创建Excel数据透视表(Pivot Table)的完整实战教程

03-19

Java Arraylist在多线程环境下的问题与解决方案

03-19

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论