1. 程式人生 > >SpringBoot2.0學習筆記:(十) Spring Boot中整合Redis

SpringBoot2.0學習筆記:(十) Spring Boot中整合Redis

一、關於Lettuce

關於在SpringBoot2.0.x版本中整合Redis,我們先看一下官方的遷移文件有什麼說的:

Spring Boot2.0遷移指南

當你使用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可以參看這篇部落格:

Centos 下安裝 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註解聲明瞭兩個元件注入到容器中去,分別是stringRedisTemplateredisTemplate,在程式碼中就是用這兩個元件來操作快取的。

這裡還要說下,當你使用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