Spring Boot 集成教程
- Spring Boot 介紹
- Spring Boot 開發環境搭建(Eclipse)
- Spring Boot Hello World (restful接口)例子
- spring boot 連接Mysql
- spring boot配置druid連接池連接mysql
- spring boot集成mybatis(1)
- spring boot集成mybatis(2) – 使用pagehelper實現分頁
- spring boot集成mybatis(3) – mybatis generator 配置
- spring boot 接口返回值封裝
- spring boot輸入數據校驗(validation)
- spring boot rest 接口集成 spring security(1) – 最簡配置
- spring boot rest 接口集成 spring security(2) – JWT配置
- spring boot 異常(exception)處理
- spring boot 環境配置(profile)切換
- spring boot redis 緩存(cache)集成
概述
本文介紹spring boot項目集成redis緩存的過程。
redis是一個開源的內存NOSQL數據庫,在web開發中主要被用於數據緩存。一般在高並發的情況下,web服務器接受訪問時,直接從數據庫加載是慢的,需要把常用數據緩存到redis中,提高加載速度和並發能力。
項目內容
創建一個spring boot項目,配置redis各相關 bean,實現幾個接口,通過兩種方式測試redis緩存:
- 以注解方式自動緩存
- RedisTemplate手動訪問redis服務器
要求
如沒有開發環境,可參考前面章節:[spring boot 開發環境搭建(Eclipse)]。
項目創建
創建spring boot項目
打開Eclipse,創建spring boot的spring starter project項目,選擇菜單:File > New > Project ...
,彈出對話框,選擇:Spring Boot > Spring Starter Project
,在配置依賴時,勾選web
、redis
,完成項目創建。
項目依賴
需要用到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
代碼實現
項目目錄結構如下圖,我們添加了幾個類,下面將詳細介紹。
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
命令開始監控:
運行結果如下:
獲取用戶信息
redis中的數據,可以看到數據通過SET
指令保存進redis了
多次獲取用戶信息,可以看到通過GET
指令從redis中讀取緩存
修改用戶信息
redis中的緩存被刪除了
測試使用RedisTemplate訪問redis服務器
redis中的數據變化