文章目录
前言
最近新项目中要使用缓存,但是发现的之前的缓存知识忘的差不多了,所以又重新梳理了一下记录下来。
Redis安装
由于Redis官方目前没有提供windows的版本,所以使用微软提供的redis版本
redis下载地址
在Springboot中引入缓存
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置缓存
在application.yml中 配置redis和cache
spring:
redis:
database: 2
lettuce:
pool:
max-wait: 1000
max-idle: 200
max-active: 200
timeout: 1000
cache:
redis:
## 不缓存null值
cache-null-values: false
在@SpringBootApplication类上加入@EnableCaching即可开启系统中的缓存
Spring Cache Abstraction 相关注解
@Cacheable触发缓存添加@CacheEvict触发移除缓存@CachePut更新缓存,但不会干扰方法的执行@Caching组合要应用于方法的多个缓存操作@CacheConfig在类级别共享一些与缓存相关的常见设置
3.简单示例
实体类User
@Data
@AllArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 6119134703242129065L;
private Integer id;
private String name;
private Integer age;
}
创建一个SimpleCacheTestService 类
@Service
@Slf4j
public class SimpleCacheTestService {
@Cacheable(cacheNames = "cache")
public User cache() {
log.info("==== 方法内查询数据 ====");
//模拟查库的接口
return new User(1, "xi", 19);
}
}
测试接口
@GetMapping("/test")
public User test() {
return simpleCacheTestService.cache();
}
第一次调用/test接口后,即可缓存结果,第二次请求时会走缓存,不会再执行方法,在redis中也可以看到缓存的结果。
C:\Users\Administrator>redis-cli -h 127.0.0.1
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> keys *
1) "cache::SimpleKey []"
127.0.0.1:6379[2]>
4.简单控制的示例
创建一个新的SimpleControllerCacheTestService类
public class SimpleControllerCacheTestService {
//先从缓存中查找,如果没有则执行方法,最后将结果放入缓存中去
@Cacheable(cacheNames = "myCache")
public User getFromCache() {
return null;
}
//@CachePut注解,它执行了方法并将返回值被放入缓存中
@CachePut(cacheNames = "myCache")
public User populateCache() {
return new User(1, "ii", 20);
}
//删除缓存
@CacheEvict(cacheNames = "myCache")
public void removeCache() {
}
}
接口
@GetMapping("/test1")
public User test1() {
User fromCache = simpleControllerCacheTestService.getFromCache();
if (fromCache == null) {
log.info("缓存为空,查库填充");
User newValue = simpleControllerCacheTestService.populateCache();
log.info("查库填充: {}", newValue);
return newValue;
}
log.info("Returning from Cache: {}", fromCache);
return fromCache;
}
@GetMapping("/remove1")
public void removeTest1(){
simpleControllerCacheTestService.removeCache();
}
调用两次/test1接口,在调用/remove1接口
INFO 10204 --- [nio-1025-exec-4] c.e.cache.controller.TestController : 缓存为空,查库填充
INFO 10204 --- [nio-1025-exec-4] c.e.cache.controller.TestController : 查库填充: User(id=1, name=ii, age=20)
INFO 10204 --- [nio-1025-exec-5] c.e.cache.controller.TestController : Returning from Cache: User(id=1, name=ii, age=20)
5.根据方法的参数或结果来插入缓存的示例
创建一个新的ControllerCacheTestService类
@Service
@CacheConfig(cacheNames = "ControllerCache")
@Slf4j
public class ControllerCacheTestService {
private static final String CONTROLLED_PREFIX = "user_";
public static String getCacheKey(String relevant) {
return CONTROLLED_PREFIX + relevant;
}
@Cacheable(key = "'user_'.concat(#id)", unless = "#result==null")
public User getFromCache(Integer id, String name) {
log.info("=== getFromCache ===");
return null;
}
@CachePut(key = "'user_'.concat(#result.id)")
public User addUser(Integer id, String name, Integer age) {
log.info("=== addUser ===");
return new User(id, name, age);
}
@CacheEvict(key = "T(com.example.cache.service.ControllerCacheTestService).getCacheKey(#id)")
public void delete(Integer id) {
log.info("=== delete ===");
}
}
- 缓存key的生成方式使用的是 SpEL表达式,
- 在redis中存储的
key = cacheNames+key #result方法返回的结果
接口测试
@GetMapping("/test2")
public User test2(){
return controllerCacheTestService.getFromCache(1,"iii");
}
@GetMapping("/test3")
public User test3(){
return controllerCacheTestService.addUser(1,"iii",20);
}
@GetMapping("/remove2")
public void removeTest2(){
controllerCacheTestService.delete(1);
}
缓存的TTL(过期时间)
1.全局的TTL设置
cache:
redis:
## 缓存的存活时间 100 秒
time-to-live: 100s
2.对某些cacheNames进行定制
考虑到后期扩展,所以将需要定制的cacheName 写入到配置文件中 去。
spring:
cache:
##定制缓存 的到期时间
custom:
cache-key-expire-times:
##myCache 的缓存时间为20s
myCache: 20s
定义一个spring.cache.custom.cacheKeyExpireTimes 的key,值为Map(cacheName->过期时间).
创建一个配置文件CacheConfigurationProperties用于读取这些配置
@Data
@ConfigurationProperties(prefix = "spring.cache.custom")
public class CacheConfigurationProperties {
private Map<String, Duration> cacheKeyExpireTimes = new HashMap<>();
}
配置CacheManager
@Configuration
@Import({CacheConfigurationProperties.class, CacheProperties.class})
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheConfigurationProperties properties, CacheProperties cacheProperties) {
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
for (Map.Entry<String, Duration> cacheNameAndTimeout : properties.getCacheKeyExpireTimes().entrySet()) {
cacheConfigurations.put(cacheNameAndTimeout.getKey(), createCacheConfiguration(cacheNameAndTimeout.getValue()));
}
return RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(createCacheConfiguration(cacheProperties.getRedis().getTimeToLive()))
.withInitialCacheConfigurations(cacheConfigurations).build();
}
private static RedisCacheConfiguration createCacheConfiguration(Duration timeoutInSeconds) {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeoutInSeconds);
}
}
设置缓存对象的值为JSON 而不是序列化后的值
/**
* @author peter
* date: 2019-10-16 09:23
**/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(getObjectJackson2JsonRedisSerializer())
);
}
private Jackson2JsonRedisSerializer<Object> getObjectJackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.registerModule(new Hibernate5Module()); // new module, NOT JSR310Module;
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
objectMapper.enable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
serializer.setObjectMapper(objectMapper);
return serializer;
}
}
需要添加的依赖
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
使用缓存方法的注意事项
绝对不要在同一类中调用缓存的方法。 因为 Spring Cache 代理了缓存方法的访问,以使Cache Abstraction起作用,在同一个类中调用时会使代理失效。
3131

被折叠的 条评论
为什么被折叠?



