Spring Boot 2.2.1.RELEASE
1. Spring 缓存抽象
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术
Spring框架提供了对将缓存透明添加到现有Spring应用程序的支持。与事务支持类似,缓存抽象允许一致使用各种缓存解决方案,而对代码的影响最小。
每次调用需要缓存功能的方法时,Spring会检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户,下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点:
- 确定方法需要被缓存以及他们的缓存策略;
- 缓存中读取之前缓存存储的数据
缓存注解和接口
名称 | 解释 |
---|---|
@EnableCaching | 开启基于注解的缓存 |
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等\ |
CacheManager | 缓存管理器,管理各种缓存(cache)组件 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@CacheConfig | 在类级别共享一些与缓存相关的常见设置 |
@Cacheable | 创建缓存,主要针对方法配置,根据方法的请求参数对其进行缓存 |
@CachePut | 更新缓存,而不会干扰方法的执行。与@Cacheable区别在于是否每次都调用方法,常用于更新 |
@CacheEvict | 清空缓存 |
@Cacheable/@CachePut/@CacheEvict 主要的参数
名称 | 解释 |
---|---|
value/cacheNames | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:@Cacheable(value=”mycache”) @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:@Cacheable(value=”testcache”,key=”#id”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
unless | 否定缓存。当条件结果为TRUE时,就不会缓存。 @Cacheable(value=”testcache”,unless=”#userName.length()>2”) |
allEntries(@CacheEvict ) | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation(@CacheEvict) | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。 例如:@CachEvict(value=”testcache”,beforeInvocation=true) |
2. Spring Cache + Redis 使用
添加Spring Data Redis相关依赖,Cache在Spring框架包中,不用单独添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
application.yml 添加Cache和Redis环境配置
server:
port: 8123
spring:
cache:
type: redis
redis:
# Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
database: 0
host: localhost
port: 6379
# 连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1
# 连接池中的最大空闲连接 默认 8
max-idle: 8
# 连接池中的最小空闲连接 默认 0
min-idle: 0
添加配置
自定义key生成器,改变默认key生成策略
自定义缓存管理器,设置缓存时间,更改key和value序列化器
自定义redisTemplate,修改key和value的序列化器
@Configuration
// 开启基于注解的缓存
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* Spring Cache 自定义key生成器,缓存数据时key生成策略
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return ((target, method, params) -> {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(target.getClass().getName());
stringBuilder.append(method.getName());
for (Object obj : params) {
stringBuilder.append(obj.toString());
}
return stringBuilder.toString();
});
}
/**
* Spring Cache 自定义缓存管理器,管理各种缓存(cache)组件
* @param connectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认超时时间5分钟
redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(5L))
// 如果是空值,不缓存
.disableCachingNullValues()
// 设置key序列化器
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value序列化器
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((new GenericFastJsonRedisSerializer())));
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
return cacheManager;
}
/**
* Spring data Redis 自定义redisTemplate
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericFastJsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericFastJsonRedisSerializer());
return template;
}
}
测试用户类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private int id;
private String name;
}
定义User接口
public interface UserService {
User getUser(Integer id);
List<User> getUserList();
User updateUser(Integer id);
void clearUserCache(Integer id);
void clearAllCache();
}
在User实现类中使用缓存注解
cacheNames 即 value,定义缓存的名称,创建、更新和清除缓存都针对的缓存名
key 和 cacheNames 拼成 cacheNames::key,即为保存在Redis中的key
@Service
public class UserServiceImpl implements UserService {
@Override
@Cacheable(cacheNames = "user", key = "#id")
public User getUser(Integer id) {
User user = null;
if (id == 1) {
user = new User(1, "张一 key 1");
} else if (id == 2) {
user = new User(2, "张二 key 2");
} else if (id == 3) {
user = new User(3, "张三 key 3");
}
return user;
}
@Override
@Cacheable(cacheNames = "userList")
public List<User> getUserList() {
List<User> userList = new ArrayList<>();
User user1 = new User(1, "张三");
User user2 = new User(2, "李四");
User user3 = new User(3, "王五");
userList.add(user1);
userList.add(user2);
userList.add(user3);
return userList;
}
@Override
// condition 定义更新条件,只有id为1才更新
@CachePut(cacheNames = "user", condition = "#id == 1", key = "#id")
public User updateUser(Integer id) {
return new User(id, "更新 " + id + " 名称");
}
@Override
// 根据user的id清空具体的id的缓存
@CacheEvict(cacheNames = "user", key = "#id")
public void clearUserCache(Integer id) {
}
@Override
// 清空user下所有的缓存
@CacheEvict(cacheNames = "user", allEntries=true)
public void clearAllCache() {
}
}
3. 测试
User的测试Controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUser")
public User getUser(@RequestParam("id") Integer id){
return userService.getUser(id);
}
@GetMapping("/getUserList")
public List<User> getUserList(){
return userService.getUserList();
}
@GetMapping("/updateUser")
public User updateUser(@RequestParam("id")Integer id) {
return userService.updateUser(id);
}
@GetMapping("/clearUserCache")
public void clearUserCache(@RequestParam("id") Integer id){
userService.clearUserCache(id);
}
@GetMapping("/clearAllCache")
public void clearAllCache(){
userService.clearAllCache();
}
}
创建user缓存
localhost:8123/user/getUser?id=1
指定了key,所以没有走生成key的方法;再次调用就没有走到具体的service方法中,直接缓存获取
创建userList缓存
localhost:8123/user/getUserList
未指定key,走自定义生成key
更新user缓存
localhost:8123/user/updateUser?id=1
id为2和3,缓存没更新
清除user缓存
localhost:8123/user/clearUserCache?id=2
id为2的user缓存清空了
清除所有user缓存
localhost:8123/user/clearAllCache
只有cacheNames为user的缓存都被清除了,缓存名为userList的没有清除
所有可以使用@Caching 定义类级别的缓存名