Spring 缓存使用

Spring 缓存使用

生效原理

抽象而不是实现

Spring框架中, 缓存服务是一组抽象, 也就是一组API, 由

  • org.springframework.cache.Cache
  • org.springframework.cache.CacheManager

抽象. 抽象中并不包含多线程或者多进程处理逻辑, 这些逻辑应该由缓存实现方提供. Spring data工程默认提供了一些实现, 例如ConcurrentHashMap, GemFire, Ehcache等.

通过在方法级别上加@Cacheable等注解, 每当方法被调用的时候, Spring通过AOP的方式拦截方法的执行, 执行缓存的添加, 更新, 删除等操作(取决于注解属性的配置和注解类型).

声明式缓存

在Spring中使用缓存的方式是声明式缓存, 只需要三步配置即可启用:

  • 缓存声明 
    在需要缓存的方法上以注解的形式标识.
  • 缓存配置 
    显式或隐式配置缓存数据存储后端(Redis, ConcurrentHashMap等).
  • @Configuration配置类中加上@EnableCaching, 在应用范围内启用注解.

声明式缓存的类型

共有以下类型:

  • @Cacheable 查询缓存
  • @CacheEvict 删除缓存条目
  • @CachePut 更新缓存条目
  • @Caching
  • @CacheConfig

三级缓存策略定制

从上到下, 依次可以进行三次缓存策略的设定, 每一层都会覆盖上层的默认设定:

  • 全局范围的定制: 配置在CacheManagerKeyGenerator中.
  • 类级别的定制: 使用@CacheConfig注解;
  • 方法级别的定制.

Key生成策略

说到底最终还是要以key-value的形式写到后端存储中, 那么Spring的Key生成策略就是值得考虑的. Spring默认的KeyGenerator使用以下算法:

  • 如果没有参数, 返回SimpleKey.EMPTY
  • 如果只有一个参数, 直接返回参数实例.
  • 如果有多个参数, 那么返回一个包含所有参数的SimpleKey.

那么也就是说, 对于cacheNames相同的方法, 如果他们调用时的参数类型和值也相同, 那么就会认为使用的是同一条缓存条目:

@Cacheable(cacheNames = "girl")
public Girl findOne(Integer id) {
    logger.error("方法findOne被调用!");
    return girlRepository.findOne(id);
}

@Cacheable(cacheNames = "girl")
public Girl findOne1(Integer id) {
    logger.error("方法findOne1被调用!");
    return girlRepository.findOne(id);
}

// Test类中
@Test
public void cacheTest() {
    girlService.findOne(14);
    girlService.findOne1(14); // 经测试, 第二条调用不会触发数据库操作
}

缓存添加–@Cacheable的使用

通常用来标识查询缓存, 也就是数据库的select一类的不会改变目标对象状态的情景.

以下是一个使用的例子:

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

我们可以通过指定自定义类型的key, 来显式设定缓存的key, 默认是包含方法的所有参数构建的.

同步式缓存

默认缓存策略是不会加锁的, 也就是多线程执行同一个方法的时候, 对于同样参数的调用, 可能仍会被计算多次. 我们可以通过在声明式缓存中配置sync属性, 显式告知CacheManager, 使用同步(阻塞式)策略更新缓存, 也就是多线程下对同一方法的同一参数的调用会被阻塞, 只有一个运行, 其他的线程在锁释放后使用缓存.

@Cacheable(cacheNames="foos", sync="true")
public Foo executeExpensiveOperation(String id) {...}
  • 1
  • 2

条件缓存

通过在声明式缓存中添加conditionunless属性, 可以指明缓存只有在符合特定条件下才生效, 并且在执行完后显式排除掉部分结果.

@Cacheable(cacheNames="book", condition="#name.length < 32", unless="#result.hardback")
public Book findBook(String name)
  • 1
  • 2

注意: 

  • condition是在方法执行前评估, unless是在方法执行后评估.
  • 关于声明式缓存中能够使用的SpEL类型, 详见文章开头的链接.

缓存更新–@CachePut的使用

通常用来表明该方法的执行应引起缓存条目的更新, 类比于sql的update操作.

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
  • 1
  • 2

缓存清除–@CacheEvict的使用

通常用来表明方法的执行应引起相应缓存条目(或相应缓存主题整体)的清除.

@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)
  • 1
  • 2

上面allEntries指明是对相应缓存主题整体或者key对应条目的清除.

多缓存策略–@Caching的使用

允许多个不冲突的缓存策略作为一个缓存策略组同时生效.

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
  • 1
  • 2

统一声明缓存–@CacheConfig的使用

类级别的注解, 用于描述类内方法共享的缓存主题名, 缓存key, CacheManagerCacheResolver等.

@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Spring Boot中对缓存的增强支持

对缓存后端的适配选择更多了, 通常使用的是Redis.

可以通过spring.cache.type显式指定要使用的后端类型, 或者Spring Boot会在路径中自动搜寻缓存后端提供者:

  • 如果找到了, 那么自动创建相应类型的CacheManager实现类, 例如RedisCacheManager.
  • 如果没有找到, 那么默认会使用ConcurrentHashMap作为缓存后端.
  • 如果想要临时关闭缓存, 可以显式指定spring.cache.type=none来关闭缓存操作. 通常用在测试场景中.

定制CacheManager–添加缓存过期时间等

我们也可以通过Java Bean的方式来对Spring Boot自动配置的缓存后端进一步定制, 如设定缓存主题名:

@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
    return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
        @Override
        public void customize(ConcurrentMapCacheManager cacheManager) {
            cacheManager.setCacheNames(Arrays.asList("one", "two"));
        }
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们可以通过为@Bean添加额外的@Order注解来多次增强配置同一个CacheManager.

一些需要特别说明的点

  • Spring推荐只在具体类的public方法上使用@Cache*注解 
    因为Java中接口的注解是不会被继承到类上的, 所以如果是通过cglib技术进行的动态代理不会触发缓存效果, 此外, 通过字节码织入实现的切面技术也不会触发缓存效果. 所以最好是在具体类的public方法上声明@Cache*.
<think> 首先,用户提到了站内引用 "[^1]:SpringBoot缓存注解的使用。在springboot中使用缓存注解很简单,首先就只需要在依赖中引入spring-boot-starter-cache依赖,然后在application类中使用@EnableCaching注解开启缓存,然后在方法上根据需要使用具体的缓存注解即可。 [^1]。" 这似乎是缓存注解的基本使用方法。 用户的问题是:"我想查找Spring缓存注解的使用方法 请问Spring缓存注解的使用方法"。这是一个直接的查询,用户想了解Spring缓存注解的使用方法。 系统级指令要求: - 所有行内数学表达式使用$...$格式(但这里可能不涉及数学表达式,因为主题是Spring缓存注解)。 - 独立公式使用$$...$$并单独成段(同样,可能不适用)。 - LaTeX语法正确(但主题是编程,不是数学)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 "[^1]"。 用户提到了参考站内引用,所以我必须引用提供的引用 "[^1]"。引用中说:"在springboot中使用缓存注解很简单,首先就只需要在依赖中引入spring-boot-starter-cache依赖,然后在application类中使用@EnableCaching注解开启缓存,然后在方法上根据需要使用具体的缓存注解即可。" 所以,我应该基于这个构建回答。 此外,系统指令说:"上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。" 用户上一次的问题是关于Spring缓存注解的使用方法,所以我需要聚焦于此。 结构化回答: - 回答应该结构清晰,帮助用户逐步解决问题。 - 确保回答真实可靠。基于我对Spring缓存注解的了解: - 核心注解包括: - `@EnableCaching`:用于启动类或配置类来启用缓存。 - `@Cacheable`:方法调用结果被缓存。 - `@CacheEvict`:清除缓存。 - `@CachePut`:更新缓存。 - `@Caching`:组合多个缓存操作。 - 需要添加依赖:spring-boot-starter-cache。 - 配置缓存管理器。 - 示例代码。 用户指定了“Spring缓存注解”,但引用中提到的是“SpringBoot”,我应该保持一致,因为Spring Boot是Spring Framework的一部分。 回答结构: 1. **引言**:简要介绍Spring缓存注解。 2. **步骤**:逐步说明如何使用。 - 添加依赖。 - 启用缓存。 - 使用具体注解。 3. **示例**:提供代码示例。 4. **引用**:末尾添加引用标识。 5. **生成相关问题**:最后一部分。 生成相关问题: - 基于主题,如性能优化、常见问题等。 - 例如:如何配置不同的缓存提供者?缓存注解的常见问题?等。 使用中文回答。 大纲: - 解释Spring缓存注解的作用。 - 详细步骤: - 1. 添加依赖(如引用中提到)。 - 2.Spring Boot应用类中添加`@EnableCaching`。 - 3. 在方法上使用缓存注解如`@Cacheable`, `@CacheEvict`。 - 提供简单示例代码。 - 结束时添加引用。 - 然后生成相关问题。 引用标识:在回答中提到引用内容时添加 "[^1]",但引用是用户提供的,所以我需要自然地融入。 用户说:"参考站内引用:引用[1]:..." 所以我应该在回答中引用 "[^1]"。 最后,生成相关问题部分:"§§相关问题§§" 后列出几个问题。 完整回答: - 先回答用户的查询。 - 然后添加相关问题。 </think> ### Spring缓存注解的使用方法 Spring框架提供了强大的缓存抽象,允许开发者轻松地在应用中集成缓存功能,以提高性能。缓存注解基于Spring的AOP(面向切面编程)实现,主要适用于Spring Boot项目。以下是详细的使用方法,逐步分解: #### 1. **添加依赖** 首先,在Spring Boot项目中,需要引入Spring缓存模块依赖。这通常在项目的`pom.xml`(Maven项目)或`build.gradle`(Gradle项目)中完成。核心依赖是`spring-boot-starter-cache`: - **Maven示例**: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> ``` - **Gradle示例**: ```groovy implementation 'org.springframework.boot:spring-boot-starter-cache' ``` 添加此依赖后,Spring Boot会自动配置缓存的基础设施[^1]。 #### 2. **启用缓存** 在Spring Boot应用的主类(或配置类)上添加`@EnableCaching`注解,以激活缓存功能。这个注解告诉Spring扫描项目中所有缓存相关的注解: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching // 启用Spring缓存 public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 启用后,Spring会基于默认配置(如使用ConcurrentMap缓存)工作,但你可以自定义缓存提供者(如Redis、Ehcache)[^1]。 #### 3. **使用具体缓存注解** 在方法级别应用缓存注解,Spring会在方法执行时自动处理缓存逻辑。以下是核心注解及其用法: - **`@Cacheable`**:标记方法的结果应该被缓存。如果第一次调用结果已缓存,后续调用直接返回缓存值,跳过方法执行。 - **参数**: - `value` 或 `cacheNames`:指定缓存名称(如 `"userCache"`)。 - `key`:可选,定义缓存键(支持SpEL表达式)。 - **示例**:缓存用户查询结果。 ```java import org.springframework.cache.annotation.Cacheable; @Service public class UserService { @Cacheable(value = "users", key = "#id") // 缓存键为id public User getUserById(Long id) { // 模拟数据库查询 return userRepository.findById(id).orElse(null); } } ``` - **`@CacheEvict`**:清除缓存条目,常用于更新或删除操作。 - **参数**: - `value`:缓存名称。 - `key`:指定要清除的键。 - `allEntries`:可选,是否清除整个缓存(默认为false)。 - **示例**:删除用户时清除缓存。 ```java @CacheEvict(value = "users", key = "#id") // 清除指定id的缓存 public void deleteUser(Long id) { userRepository.deleteById(id); } ``` - **`@CachePut`**:更新缓存内容,但始终执行方法(适合写入操作)。 - **示例**:更新用户信息并刷新缓存。 ```java @CachePut(value = "users", key = "#user.id") public User updateUser(User user) { return userRepository.save(user); } ``` - **`@Caching`**:组合多个缓存操作(如同时使用`@Cacheable`和`@CacheEvict`)。 - **示例**:复杂操作。 ```java @Caching( evict = {@CacheEvict(value = "users", allEntries = true)}, put = {@CachePut(value = "users", key = "#user.id")} ) public User updateAndRefresh(User user) { // 业务逻辑 } ``` #### 4. **配置缓存提供者** Spring支持多种缓存实现(如Redis、Caffeine)。通过在`application.properties`或`application.yml`中配置,切换缓存提供者: - **示例(使用Redis)**: ```properties # application.properties spring.cache.type=redis spring.redis.host=localhost spring.redis.port=6379 ``` 然后添加Redis依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` #### 注意事项 - **键生成策略**:默认使用方法的参数作为键,但可以通过`keyGenerator`自定义。 - **缓存条件**:使用`condition`参数(SpEL表达式)控制何时缓存,例如`@Cacheable(value="users", condition="#id > 10")`。 - **性能优化**:缓存注解能显著减少数据库访问,但需注意缓存一致性(如使用`@CacheEvict`处理更新)。 - **测试**:在开发中,使用`spring.cache.type=none`临时禁用缓存进行调试。 通过以上步骤,您可以轻松地在Spring Boot项目中集成缓存功能。Spring缓存注解简化了缓存管理,提升了应用响应速度[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值