StringBoot中使用Caffeine/Redis缓存
2021-05-19 17:38:17
Caffeine 是基于Java 8 的一个高性能本地缓存库
## 添加依赖
`pom.xml` 中添加
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- https://github.com/ben-manes/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.8</version>
</dependency>
```
## 配置缓存
配置缓存类型`application.properties`
```
# 缓存类型
spring.cache.type=caffeine
# 初始缓存空间大小, 缓存的最大条数, 过期时间
# 这种方式不方便针对每个cache配置不同的参数,推荐使用下面的CacheConfig类来配置
# spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=30s
```
添加一个CacheConfig类,配置缓存空间名称和过期时间,示例中配置了两种缓存(bookCache、findUserByToken)
```
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@Configuration
public class CacheConfig {
private static final int DEFAULT_MAXSIZE = 50000; // 缺省最大容量50000
private static final int DEFAULT_TTL = 10; // 缺省10秒
/**
* 定义cache名称、过期时间(秒)、最大容量
* 每个cache缺省:10秒超时、最多缓存50000条数据,需要修改可以在构造方法的参数中指定
*/
public enum Caches {
bookCache(60, 50), // 图书缓存(60秒,最大缓存条数50)
findUserByToken(30, 100), // 通过token查用户,缓存30秒,最大容量100条
;
Caches() {
}
Caches(int ttl) {
this.ttl = ttl;
}
Caches(int ttl, int maxSize) {
this.ttl = ttl;
this.maxSize = maxSize;
}
private int maxSize = DEFAULT_MAXSIZE; // 最大數量
private int ttl = DEFAULT_TTL; // 过期时间(秒)
public int getMaxSize() {
return maxSize;
}
public int getTtl() {
return ttl;
}
}
/**
* 创建基于 Caffeine 的 Cache Manager
*/
@Bean
@Primary
public CacheManager caffeineCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
ArrayList<CaffeineCache> caches = new ArrayList<>();
for (Caches c : Caches.values()) {
caches.add(new CaffeineCache(c.name(),
Caffeine.newBuilder().recordStats()
.expireAfterWrite(c.getTtl(), TimeUnit.SECONDS)
.maximumSize(c.getMaxSize())
.build())
);
}
cacheManager.setCaches(caches);
return cacheManager;
}
}
```
开启缓存
```
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
```
## 编码方式使用缓存
```
import org.springframework.cache.CacheManager;
@Autowired
CacheManager cacheManager;
Cache cache = cacheManager.getCache("bookCache");
// 把book对象放入缓存,key为book:1
cache.put("book:1", new Book(1, "Java语言规范"));
// 从缓存中获取数据
Cache.ValueWrapper valueWrapper = cache.get("book:1");
if(valueWrapper != null) {
System.out.println(valueWrapper.get());
}
// 删除缓存
cache.evict("book:1");
```
## 注解方式使用缓存
在Service的方法中开启缓存
```
@Cacheable(cacheNames = "bookCache", key = "'bookId:' + #id")
public Book findBookById(int id) {
System.out.println("query book from db");
return bookMapper.findById(id);
}
@Cacheable(cacheNames = "findUserByToken", key = "targetClass + #token")
public User findUserByToken(String token) {
System.out.println("query user from db");
return userMapper.findByToken(token);
}
```
`@Cacheable`的`cacheNames`(也可以用别名`value`)表示在配置中定义的缓存空间名称,`key`为缓存的标识,用于区分不同的缓存数据(缺省按照方法的`所有参数进行组合`),可以使用`targetClass`、`methodName`分别代表当前类名和方法名
清除缓存
```
// 指定key
@CacheEvict(cacheNames = "findUserByToken", key = "targetClass + #token")
// 全部
@CacheEvict(cacheNames = "findUserByToken", allEntries = true)
```
缓存对象需要可序列化
```
public class Book implements Serializable {}
public class User implements Serializable {}
```
## 切换为Reids缓存
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
### application.properties
```
spring.cache.type=redis
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=
```
### CacheConfig
```
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
@Configuration
public class CacheConfig {
private static final int DEFAULT_MAXSIZE = 50000; // 缺省最大容量50000
private static final int DEFAULT_TTL = 10; // 缺省10秒
/**
* 定义cache名称、过期时间(秒)、最大容量
* 每个cache缺省:10秒超时、最多缓存50000条数据,需要修改可以在构造方法的参数中指定
*/
public enum Caches {
bookCache(60, 50), // 图书缓存(60秒,最大缓存条数50)
findUserByToken(30, 100), // 通过token查用户,缓存30秒,最大容量100条
;
Caches() {
}
Caches(int ttl) {
this.ttl = ttl;
}
Caches(int ttl, int maxSize) {
this.ttl = ttl;
this.maxSize = maxSize;
}
private int maxSize = DEFAULT_MAXSIZE; // 最大數量
private int ttl = DEFAULT_TTL; // 过期时间(秒)
public int getMaxSize() {
return maxSize;
}
public int getTtl() {
return ttl;
}
}
/**
* 创建基于 Redis 的 Cache Manager
*/
@Bean
@Primary
public CacheManager caffeineCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(DEFAULT_TTL)));
for (Caches c : Caches.values()) {
builder.withCacheConfiguration(c.name(), RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(c.getTtl())));
}
return builder.build();
}
}
```