SpringBoot2.0學習筆記:(十) Spring Boot中整合Redis
一、關於Lettuce
關於在SpringBoot2.0.x版本中整合Redis,我們先看一下官方的遷移文件有什麼說的:
當你使用spring-boot-starter-redis的時候,Lettuce現已取代Jedis作為Redis驅動。當你使用更好級別的Spring資料結構時,你會發現變化時清晰的。我們仍然支援Jedis,並且你可以任意切換依賴機制,通過排除io.lettuce:lettuce-core和新增redis.clients.jedis的方式。
Lettuce現已取代Jedis作為Redis驅動。
那Lettuce又是個什麼呢?與Jedis又有何區別呢?
Lettuce 是一個可伸縮的執行緒安全的 Redis 客戶端,支援同步、非同步和響應式模式。多個執行緒可以共享一個連線例項,而不必擔心多執行緒併發問題。它基於優秀 Netty NIO 框架構建,支援 Redis 的高階功能,如 Sentinel,叢集,流水線,自動重新連線和 Redis 資料模型
Jedis在實現上是直接連線的redis server,如果在多執行緒環境下是非執行緒安全的,這個時候只有使用連線池,為每個Jedis例項增加物理連線 Lettuce的連線是基於Netty的,連線例項(StatefulRedisConnection)可以在多個執行緒間併發訪問,應為StatefulRedisConnection是執行緒安全的,所以一個連線例項(StatefulRedisConnection)就可以滿足多執行緒環境下的併發訪問,當然這個也是可伸縮的設計,一個連線例項不夠的情況也可以按需增加連線例項.
二、SpringBoot整合Redis
對於SpringBoot整合Redis來說,我們需要匯入兩個包,即:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.3</version>
</dependency>
之後再配置檔案中配置與redis有關的屬性
spring.redis.host=192.168.31.5
spring.redis.port=6379
spring.redis.password=redis
#連線超時時間(毫秒)
spring.redis.timeout=10000ms
# 連線池最大連線數(使用負值表示沒有限制) 預設 8
spring.redis.lettuce.pool.max-active=16
# 連線池中的最大空閒連線 預設 8
spring.redis.lettuce.pool.max-idle=8
# 連線池最大阻塞等待時間,單位毫秒(使用負值表示沒有限制) 預設 -1
spring.redis.lettuce.pool.max-wait=1000ms
# 連線池中的最小空閒連線 預設 0
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.shutdown-timeout=100ms
這樣關於redis的屬性就配置好了,我們可以測試一下:
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate redisTemplate;
@Test
public void contextLoads() {
stringRedisTemplate.opsForValue().append("append","asss");
String append = stringRedisTemplate.opsForValue().get("append");
System.out.println(append);
}
@Test
public void test01(){
ValueOperations<String,Object> vo = redisTemplate.opsForValue();
vo.set("redis","redisTemplate");
Object name = vo.get("redis");
System.out.println(name);
}
當然,這一切的前提是你虛擬機器中的redis連線成功了,關於在linux中安裝啟動redis可以參看這篇部落格:
這裡在配置檔案中設定屬性時,出現了一個問題:
Value ‘10000’ is not a valid duration
也就是在配置redis連線超時時間時出現的問題,解決方法就是你要在你設定的屬性值後面加上單位。
測試完成之後,我們看一下Spring Boot對Redis的自動配置:
按照慣例命名來說,redis的自動配置一般在:RedisAutoConfiguration中。
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
從註解開始看看:
- @Configuration:表明這是一個配置類
- @ConditionalOnClass(RedisOperations.class) :含有這個RedisOperations類才開始redis自動配置,RedisOperations是spring-data-redis jar包中的,在引入pring-boot-starter-data-redis時就引入了。
- @EnableConfigurationProperties(RedisProperties.class):開啟屬性的自動注入
- @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }):匯入這兩個配置檔案,這兩個配置檔案就是根據你匯入的jar包來判斷是用Lettuce還是Jedis來作為redis驅動的。
之後可以看出在類中使用@Bean註解聲明瞭兩個元件注入到容器中去,分別是stringRedisTemplate
和redisTemplate
,在程式碼中就是用這兩個元件來操作快取的。
這裡還要說下,當你使用stringRedisTemplate
往redis中寫入資料的話,你用redisTemplate
是讀不到的。因為這兩個元件的key與value的序列化方式是不一樣的。
進入StringRedisTemplate這個類中可以看一下:
/**
* Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
* and {@link #afterPropertiesSet()} still need to be called.
*/
public StringRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setValueSerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
setHashValueSerializer(stringSerializer);
}
可以看到其key與value的序列化是通過StringRedisSerializer實現的。
而RedisTemplate的序列化方式是:
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
......
其預設的序列化方式是JdkSerializationRedisSerializer。
三、修改RedisTemplate的序列化方式
這裡先推薦一款Redis視覺化工具:redis-desktop-manager
安裝完成開啟連線上redis之後,可以看一下之前存放的資料:
可以看到,通過RedisTemplate存放的資料經過JdkSerializationRedisSerializer序列化之後,在redis資料庫中呈現的樣式有點不符合我們的觀感,我們可以修改一下其序列化方式:
首先我們匯入一下spring-boot-starter-json這個jar包,這是SpringBoot給我們提供的轉化json包,其內部引用了jackson-databind這個jar包.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
之後參照StringRedisTemplate新建一個配置檔案:
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 設定值(value)的序列化採用FastJsonRedisSerializer。
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 設定鍵(key)的序列化採用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
在這裡,我們給RedisTemplate的key的序列化方式改為StringRedisSerializer,而value的序列化方式改為GenericJackson2JsonRedisSerializer。
之後繼續測試一下:
@Test
public void test01(){
ValueOperations<String,Object> vo = redisTemplate.opsForValue();
// vo.set("redis","redisTemplate");
// Object name = vo.get("redis");
// System.out.println(name);
Student student = new Student(101, "zhangsan", "male");
vo.set("student:"+student.getId(),student);
System.out.println(vo.get("student:"+student.getId()));
}
可以看到,現在使用RedisTemplate存放資料的話,key以String型別存放,value以json型別存放了。
這裡在輸出的時候System.out.println(vo.get("student:"+student.getId()));
遇到一個異常:
cannot deserialize from Object value (no delegate- or property-based Creator
解決辦法就是給實體類新增上無參構造器。
以上就是整個的SpringBoot2.0整合Redis的內容了,完整的程式碼以及測試內容在GitHub上:spring-boot-redis