spring boot redis 缓存(cache)集成

时间:2021-12-13 22:51:11
spring boot redis 缓存(cache)集成

Spring Boot 集成教程


概述

本文介绍spring boot项目集成redis缓存的过程。

redis是一个开源的内存NOSQL数据库,在web开发中主要被用于数据缓存。一般在高并发的情况下,web服务器接受访问时,直接从数据库加载是慢的,需要把常用数据缓存到redis中,提高加载速度和并发能力。

项目内容

创建一个spring boot项目,配置redis各相关 bean,实现几个接口,通过两种方式测试redis缓存:

  • 以注解方式自动缓存
  • RedisTemplate手动访问redis服务器

要求

  • 安装redis服务器,参考官网文档,如没有linux系统可虚拟机安装
  • JDK1.8或更新版本
  • Eclipse开发环境

如没有开发环境,可参考前面章节:[spring boot 开发环境搭建(Eclipse)]。

项目创建

创建spring boot项目

打开Eclipse,创建spring boot的spring starter project项目,选择菜单:File > New > Project ...,弹出对话框,选择:Spring Boot > Spring Starter Project,在配置依赖时,勾选webredis,完成项目创建。

spring boot redis 缓存(cache)集成

项目依赖

需要用到commons-pool2库,在pom.xml中添加依赖

		<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

项目配置

application.properties文件中配置redis服务器的连接

## REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.0.99
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0

代码实现

项目目录结构如下图,我们添加了几个类,下面将详细介绍。

spring boot redis 缓存(cache)集成

Redis Java配置(RedisConfig.java)

首先使用@EnableCaching开启以注解方式使用缓存。

然后配置redis相关的bean

  • RedisTemplate - 访问redis的bean,用于手动访问redis服务器
  • 缓存管理器 - 注解方式使用缓存的配置
  • KeyGenerator - 自定义缓存key的生成
  • Json序列化 - Json对象被缓存时的序列化
/**
* @description redis配置 配置序列化方式以及缓存管理器
*/
@EnableCaching // 开启缓存
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig { /**
* 配置自定义redisTemplate
*
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setValueSerializer(jackson2JsonRedisSerializer());
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
} /**
* json序列化
* @return
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
} /**
* 配置缓存管理器
* @param redisConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(1))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues(); // 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("timeGroup");
cacheNames.add("user"); // 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("timeGroup", config);
configMap.put("user", config.entryTtl(Duration.ofSeconds(120))); // 使用自定义的缓存配置初始化一个cacheManager
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
// 一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
} /**
* 缓存的key是 包名+方法名+参数列表
*/
@Bean
public KeyGenerator keyGenerator() {
return (target, method, objects) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append("::" + method.getName() + ":");
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
};
} }

添加实体类 User


public class User {
private long id;
private String nickname;
private String mobile; @JsonProperty(access = Access.WRITE_ONLY) //在输出的Json数据中隐藏密码,只能输入不输出
private String password;
private String role; public User(long id, String nickname, String mobile, String password, String role) {
this.id = id;
this.nickname = nickname;
this.mobile = mobile;
this.password = password;
this.role = role;
} public User() {
super();
} public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}

常用的缓存注解

  • @Cacheable - 表明对应方法的返回结果可以被缓存,首次调用后,下次就从缓存中读取结果,方法不会再被执行了。
  • @CachePut - 更新缓存,方法每次都会执行
  • @CacheEvict - 清除缓存,方法每次都会执行

添加User的服务层

因为主要的业务逻辑在服务层实现,一般会把缓存注解加在服务层的方法上。

下面几个服务层的方法会加缓存注解:

  • getUserById - 方法的返回结果会被缓存到redis,使用注解@Cacheable
  • updateUserNickname - 原始数据被更新了,废弃缓存数据,使用注解@CacheEvict

UserSevice.java 接口

public interface UserService {

    public User getUserById(long userId);
public User updateUserNickname(long userId, String nickname);
}

UserServiceImpl.java 实现类


@Service("userService")
public class UserServiceImpl implements UserService { private static final org.slf4j.Logger log = LoggerFactory.getLogger(UserServiceImpl.class); private User user = new User(1l, "abc1", "13512345678", "123456", "role-user"); @Cacheable(value = "user", key= "#userId")
@Override
public User getUserById(long userId) { log.info("加载用户信息");
return user;
} @CacheEvict(value = "user", key= "#userId")
@Override
public User updateUserNickname(long userId, String nickname) { user.setNickname(nickname); return user;
}
}

添加User的控制层


@RestController
@EnableAutoConfiguration
@RequestMapping("/user")
public class UserController { // 注入service类
@Resource
private UserService userService; // 注入RedisTemplate
@Resource
private RedisTemplate<String, Object> redis; // 读取用户信息,测试缓存使用:除了首次读取,接下来都应该从缓存中读取
@RequestMapping(value="{id}", method=RequestMethod.GET, produces="application/json")
public User getUser(@PathVariable long id) throws Exception { User user = this.userService.getUserById(id); return user;
} // 修改用户信息,测试删除缓存
@RequestMapping(value = "/{id}/change-nick", method = RequestMethod.POST, produces="application/json")
public User changeNickname(@PathVariable long id) throws Exception{ String nick = "abc-" + Math.random();
User user = this.userService.updateUserNickname(id, nick); return user;
} // 使用RedisTemplate访问redis服务器
@RequestMapping(value="/redis", method=RequestMethod.GET, produces="application/json")
public String redis() throws Exception { // 设置键"project-name",值"qikegu-springboot-redis-demo"
redis.opsForValue().set("project-name", "qikegu-springboot-redis-demo");
String value = (String) redis.opsForValue().get("project-name"); return value;
}
}

运行

Eclipse左侧,在项目根目录上点击鼠标右键弹出菜单,选择:run as -> spring boot app 运行程序。 打开Postman访问接口,

同时监控redis服务器。

监控redis服务器,使用redis-cli命令连上服务器,然后使用monitor命令开始监控:

spring boot redis 缓存(cache)集成

运行结果如下:

获取用户信息

spring boot redis 缓存(cache)集成

redis中的数据,可以看到数据通过SET指令保存进redis了

spring boot redis 缓存(cache)集成

多次获取用户信息,可以看到通过GET指令从redis中读取缓存

spring boot redis 缓存(cache)集成

修改用户信息

spring boot redis 缓存(cache)集成

redis中的缓存被删除了

spring boot redis 缓存(cache)集成

测试使用RedisTemplate访问redis服务器

spring boot redis 缓存(cache)集成

redis中的数据变化

spring boot redis 缓存(cache)集成

总结

完整代码