一、Spring Cache简介
Spring从3.1开始定义了一系列抽象接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们进行缓存开发。Spring Cache 只负责维护抽象层,具体的实现由你的技术选型来决定。将缓存处理和缓存技术解除耦合。
Spring Cache接口为缓存的组件规范定义,包含缓存的各种操作集合,并提供了各种xxxCache的实现,如RedisCache,EhCacheCache,ConcurrentMapCache等;
项目整合Spring Cache后每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过,如果有就直接从缓存中获取结果,没有就调用方法并把结果放到缓存。
二、缓存注解介绍
对于缓存声明,Spring的缓存提供了一组java注解:
-
@CacheConfig:设置类级别上共享的一些常见缓存设置。
-
@Cacheable:触发缓存写入。
-
@CacheEvict:触发缓存清除。
-
@Caching 将多种缓存操作分组
-
@CachePut:更新缓存(不会影响到方法的运行)。
三、集成Spring Cache
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. 启动类添加@EnableCaching注解
@EnableCaching注解是spring framework中的注解驱动的缓存管理功能,当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处理。
@EnableCaching
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3. 编写缓存类
@Slf4j
@Service
public class UserService {
//查询数据库后 数据添加到缓存
@Cacheable(cacheNames = "cacheManager", key = "'USER:'+#id", unless = "#result == null")
public Map<String, Object> get(String id) {
log.info("获取:" + id);
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("username", "zhangsan");
return map;
}
//清除一条缓存,key为要清空的数据
@CacheEvict(cacheNames = "cacheManager", key = "'USER:'+#id")
public void delete(String id) {
log.info("删除:" + id);
}
//修改数据后更新缓存
@CachePut(cacheNames = "cacheManager", key = "'USER:'+#userInfo['id']", unless = "#result == null")
public void update(Map<String, Object> userInfo) {
log.info("新增[修改]:" + userInfo.toString());
}
}
4. 编写测试类
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
package com.demo;
import com.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
class SpringCacheApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", "123");
userInfo.put("username", "zhangsan");
System.out.println("新增缓存--------------------------------");
userService.update(userInfo);
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("删除缓存--------------------------------");
userService.delete((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
}
}
5. 测试结果
四、使用Redis做缓存管理
1. 集成Redis
Spring Boot 2.x中 默认使用lettuce作为默认redis客户端。当然你也可以引入redisson客户端。这两种是非阻塞反应式高级redis客户端。建议放弃阻塞客户端jedis。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce 必备依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 在application.properties添加redis配置
server.port=8765
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=
spring.cache.type=redis
其他配置可通过前缀spring.cache、spring.redis进行配置
3. 修改缓存类
@Slf4j
@Service
public class UserService {
//查询数据库后 数据添加到缓存
@Cacheable(cacheNames = "redisCacheManager", key = "'USER:'+#id", unless = "#result == null")
public Map<String, Object> get(String id) {
log.info("获取:" + id);
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("username", "zhangsan");
return map;
}
//清除一条缓存,key为要清空的数据
@CacheEvict(cacheNames = "redisCacheManager", key = "'USER:'+#id")
public void delete(String id) {
log.info("删除:" + id);
}
//修改数据后更新缓存
@CachePut(cacheNames = "redisCacheManager", key = "'USER:'+#userInfo['id']", unless = "#result == null")
public void update(Map<String, Object> userInfo) {
log.info("新增[修改]:" + userInfo.toString());
}
}
4. 编写测试类
package com.demo;
import com.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
class SpringCacheApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", "123");
userInfo.put("username", "zhangsan");
System.out.println("新增缓存--------------------------------");
userService.update(userInfo);
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("删除缓存--------------------------------");
userService.delete((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
}
}
5. 测试结果
五、自定义RedisCacheManager
1、自定义RedisCacheManager
@Configuration
public class CacheConfig {
@Value("#{${spring.cache.redis.defaultTime}}")
private Integer defaultTime;
@Value("#{${spring.cache.redis.configMap}}")
private Map<String, Integer> configMap;
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
this.getRedisCacheConfigurationWithTtl(defaultTime),
this.getRedisCacheConfigurationMap()
);
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
Set<Map.Entry<String, Integer>> entries = configMap.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
redisCacheConfigurationMap.put(entry.getKey(), this.getRedisCacheConfigurationWithTtl(entry.getValue()));
}
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
)
.entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
}
2. 在application.properties添加下面配置
# 默认缓存过期时间(单位/秒)
spring.cache.redis.defaultTime=18000
# redis缓存时间配置(key:缓存名,value:过期时间/单位秒)
spring.cache.redis.configMap={"USER":18000}
3. 修改缓存类
@Slf4j
@Service
@CacheConfig(cacheNames = "USER", cacheManager = "redisCacheManager")
public class User2Service {
//查询数据库后 数据添加到缓存
@Cacheable(key = "#id")
public Map<String, Object> get(String id) {
log.info("获取:" + id);
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("username", "zhangsan");
return map;
}
//清除一条缓存,key为要清空的数据
@CacheEvict(key = "#id")
public void delete(String id) {
log.info("删除:" + id);
}
//修改数据后更新缓存
@CachePut(key = "#userInfo['id']")
public void update(Map<String, Object> userInfo) {
log.info("新增[修改]:" + userInfo.toString());
}
}
4. 编写测试类
@SpringBootTest
class SpringCacheApplicationTests {
@Autowired
private User2Service userService;
@Test
void contextLoads() {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", "123");
userInfo.put("username", "zhangsan");
System.out.println("新增缓存--------------------------------");
userService.update(userInfo);
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
System.out.println("删除缓存--------------------------------");
userService.delete((String) userInfo.get("id"));
System.out.println("获取缓存--------------------------------");
userService.get((String) userInfo.get("id"));
}
}
5. 测试结果