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(); } } ```