Spring Boot Cache
一、Spring缓存支持
Spring 框架提供一种抽象的缓存机制,定义了 org.springframework.cache.CacheManager
和 org.springframework.cache.Cache
接口用来统一不同的缓存的技术
CacheManager
是 Spring 提供的各种缓存技术抽象接口Cache
接口包含缓存的各种操作(增加、删除、获得缓存,我们一般不会直接和此接口打交道)
1.Spring支持的CacheManager
针对不同的缓存技术,需要实现不同的 CacheManager
,Spring5.x 定义了如下的 CacheManager
实现
CacheManager | 描述 |
---|---|
SimpleCacheManager | 使用简单的 Collection 来存储缓存,主要用来测试用途 |
ConcurrentMapCacheManager | 使用 ConcurrentMap 来存储缓存,是默认的 CacheManager |
EhCacheCacheManager | 使用 EhCache 作为缓存技术 |
CaffeineCacheManager | 使用caffeine 作为缓存技术(Guava Cache被替换) |
HazelcastCacheManager | 使用 Hazelcast 作为缓存技术 |
JCacheCacheManager | 支持 JCache (JSR-107) 标准的实现作为缓存技术,如 Apache Commons JCS |
RedisCacheManager | 使用 Redis 作为缓存技术 |
若想使用对应的缓存技术,则需注册对应的CacheManager
的实现 Bean(除了其他额外配置),以 EhCache
缓存技术为例:
1 |
|
2.声明式缓存注解
Spring 提供了如下注解,来声明缓存规则(注解式AOP的应用):
2.1 @Cacheable
在目标方法执行前,Spring 先查看缓存中是否有数据
若有数据,则直接返回缓存数据
若没有数据,则执行目标方法并将方法返回值放入缓存
属性详解:
key
:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合(如:@Cacheable(value="user",key="#userName")
)value
:缓存的名称,必须指定至少一个(如:@Cacheable(value="user")
或者@Cacheable(value={"user1","use2"})
)condition
:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存(如:@Cacheable(value = "user", key = "#id",condition = "#id < 10")
)
2.2 @CachePut
将方法返回值放入缓存
属性与@Cacheable
保持一致
2.3 @CacheEvit
根据条件清除缓存
属性详解:
key
、value
、condition
同上allEntries
:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存(如:@CacheEvict(value = "user", key = "#id", allEntries = true)
)beforeInvocation
: 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(如:@CacheEvict(value = "user", key = "#id", beforeInvocation = true)
)
1 |
|
2.4 @Caching
可以通过 @Caching 注解组合多个注解策略在一个方法上
1 |
|
2.5 @CacheConfig
该注解修饰类级别。
可以使用*@*CacheConfig配置方法级别上的缓存操作共享的公共属性,比如缓存操作的缓存名称、CacheManager、keyGenerator、cacheResolver,这样类中方法上的其他缓存操作注解就不用再反复配置相同的值。如:
1 |
|
操作级别上的自定义会覆盖 @CacheConfig
的自定义。因此,每个缓存操作都会有三个级别的定义:
- 全局配置,可用于
CacheManager
和KeyGenerator
- 类级别,用
@CacheConfig
- 操作级别层面
2.6 @EnableCaching
Spring 默认没有开启声明式缓存支持,可以在配置类上使用该注解进行开启,如
1 |
|
二、Spring Boot的支持
1. CacheManager 自动配置
在Spring中使用缓存技术的关键是配置CacheManager,而Spring Boot为我们自动配置了多个CacheManager的实现。
Spring Boot 的 CacheManager 的自动配置放置在 org.springframework.boot.autoconfigure.cache
包中,如下图:
在不做任何额外配置的情况下,默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager
1 | /* |
2.默认配置
Spring Boot支持以 spring.cache
为前缀的属性来配置缓存
1 | spring.cache.type= # 可选generic,ehcache,hazelcast,infinispan,jcache,redis, guava,simple,none |
在Spring Boot环境下,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在配置类使用@EnableCaching开启缓存支持即可
三、入门实例
下面将以Spring Boot默认的ConcurrentMapCacheManager作为缓存技术,来演示 Spring 声明式缓存的用法
1.创建子模块
这里我们创建一个子模块
1 | group = 'com.ray.study' |
2.引入依赖
2.1 继承父工程依赖
在父工程spring-boot-seeds
的 settings.gradle
加入子工程
1 | rootProject.name = 'spring-boot-seeds' |
这样,子工程spring-boot-07-cache-concurrentmap
就会自动继承父工程中subprojects
`函数里声明的依赖,主要包含如下依赖:
1 | implementation 'org.springframework.boot:spring-boot-starter-web' |
2.2 引入cache
相关依赖
将子模块spring-boot-07-cache-concurrentmap
的build.gradle
修改为如下内容:
1 | dependencies { |
3.数据库准备
只需要创建integrate-jpa
数据库即可
4.修改配置
4.1 修改application.yml
配置数据源和JPA
1 | server: |
4.2 CacheConfiguration
开启 Spring 声明式缓存支持
1 | /** |
5.业务实现
5.1 entity
BaseDO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.ray.study.springboot07concurrentmap.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.util.Date;
/**
* BaseDO
*
*/
// 声明子类可继承基类字段
public class BaseDO {
private Long id;
private Date creationDate;
private Date lastUpdateDate;
}User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.ray.study.springboot07concurrentmap.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* 用户模型
*/
public class User extends BaseDO{
/**
* 用户名
*/
private String name;
/**
* 年龄
*/
private Integer age;
}
5.2 UserRepository
1 | package com.ray.study.springboot07concurrentmap.repository; |
5.3.service
UserService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.ray.study.springboot07concurrentmap.service;
import com.ray.study.springboot07concurrentmap.entity.User;
/**
* description
*
*/
public interface UserService {
User save(User user);
void remove(Long id);
User findByNameAndAge(User user);
}UserServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51package com.ray.study.springboot07concurrentmap.service.impl;
import com.ray.study.springboot07concurrentmap.entity.User;
import com.ray.study.springboot07concurrentmap.repository.UserRepository;
import com.ray.study.springboot07concurrentmap.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* description
*
*/
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
UserRepository userRepository;
public User save(User user) {
User u = userRepository.save(user);
log.info("新增:缓存用户,用户id为:{}", u.getId());
return u;
}
public void remove(Long id) {
log.info("删除:删除缓存,用户id为:{}",id);
userRepository.deleteById(id);
}
public User findByNameAndAge(User user) {
user = userRepository.findByNameAndAge(user.getName(), user.getAge());
log.info("查询:缓存用户,用户id为:{}",user.getId());
return user;
}
}
6.单元测试
- UserServiceTest
1 | package com.ray.study.springboot07concurrentmap.service; |