继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

SpringBoot 中应该怎样使用 Redis ?

2019-03-01 15:43:116907浏览

张勤一

6实战 · 22手记 · 14推荐
TA的实战

环境信息

  • JDK 版本信息
<properties>
  <java.version>1.8</java.version>
</properties>
  • SpringBoot 版本、依赖信息
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.2.RELEASE</version>
  <relativePath/>
</parent>

我们在 SpringBoot 中使用 Redis 时,会引入如下的 redis starter

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

这个 starter 引入了 jedis 和 spring-data-redis 两个与 redis 核心的包。

一些核心类的定义

  • RedisConnection

RedisConnection 处理了与 redis 后端的通信。它提供了底层的方法,可以与redis通信,这些方法的输入和返回都是二进制的值,即 byte[](字节数组)。
如果需要直接使用 RedisConnection,则需要在调用方法时,自己处理序列化和反序列化的问题。

  • RedisTemplate

**与 RedisConnection 相比,RedisTemplate 提供了较为抽象的方法。我们在使用 RedisTemplate 时,通常需要自己去指定 key 和 value 的序列化器。如果没有指定,则
使用默认的 JdkSerializationRedisSerializer(JDK 的序列化策略),可以查看其源码如下: **

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {

	...

	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
	@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
	private RedisSerializer<String> stringSerializer = new StringRedisSerializer();

  ...

	@Override
	public void afterPropertiesSet() {

		super.afterPropertiesSet();

		boolean defaultUsed = false;

		if (defaultSerializer == null) {

			defaultSerializer = new JdkSerializationRedisSerializer(
					classLoader != null ? classLoader : this.getClass().getClassLoader());
		}

		...

		initialized = true;
}
  • StringRedisTemplate

最常用的 redis 客户端,用于存取 key 和 value 都是字符串类型的数据。默认采用 String 的序列化策略(StringRedisSerializer),可以查看其源码如下:

public class StringRedisTemplate extends RedisTemplate<String, String> {

	/**
	 * 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);
	}

  ...
}

RedisConnection、RedisTemplate 和 StringRedisTemplate 的使用实例

  • Java Object(CityInfo)定义
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * <h1>城市信息</h1>
 * Created by Qinyi.
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CityInfo implements Serializable {

    /** 城市 */
    private String city;

    /** 经度 */
    private Double longitude;

    /** 纬度 */
    private Double latitude;
}
  • Redis 的配置类:RedisConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * <h1>Redis 配置</h1>
 * Created by Qinyi.
 */
@Configuration
public class RedisConfig {

    private final RedisConnectionFactory redisConnectionFactory;

    @Autowired
    public RedisConfig(RedisConnectionFactory redisConnectionFactory) {
        this.redisConnectionFactory = redisConnectionFactory;
    }

    /**
     * <h2>实例化 RedisConnection</h2>
     * SpringBoot 会自动实例化 RedisConnectionFactory, 但是不会自动实例化 RedisConnection
     * */
    @Bean("RedisConnection")
    RedisConnection getConn(){
        return redisConnectionFactory.getConnection();
    }

    /**
     * <h2>配置 RedisTemplate key/value 的序列化方式</h2>
     * */
    @Bean("RedisTemplate")
    RedisTemplate getRedisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        // RedisTemplate key/value 默认的序列化策略
        JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer();

        // StringRedisTemplate key/value 默认的序列化策略
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();

        template.setConnectionFactory(factory);
        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(redisSerializer);

        return template;
    }
}
  • 测试用例:RedisOpTest
import com.alibaba.fastjson.JSON;
import com.imooc.ad.Application;
import com.imooc.ad.vo.CityInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.test.context.junit4.SpringRunner;

import java.nio.charset.StandardCharsets;

/**
 * <h1>Redis 操作测试用例</h1>
 * Created by Qinyi.
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class},
        webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class RedisOpTest {

    /** 注入 RedisConnection (RedisConfig 中配置)*/
    @Autowired
    @Qualifier("RedisConnection")
    private RedisConnection connection;

    /** 注入 RedisTemplate (RedisConfig 中配置), 自定义了 key 和 value 的序列化器 */
    @Autowired
    @Qualifier("RedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;

    /** 注入 StringRedisTemplate, 使用默认配置 */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testUseRedisConnection() {

        String key = "RedisConnection";

        // -----------------------------------使用 RedisConnection 存取 String 对象---------------------------------------

        String value = "qinyi";

        connection.set(key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8));

        byte[] ret = connection.get(key.getBytes(StandardCharsets.UTF_8));
        assert ret != null;
        System.out.println(new String(ret));

        connection.flushAll();  // 慎用

        // -----------------------------------使用 RedisConnection 存取 Java 对象-----------------------------------------

        CityInfo cityInfo = CityInfo.builder()
                .city("suzhou").longitude(116.58).latitude(33.38).build();

        JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
        connection.set(key.getBytes(StandardCharsets.UTF_8), serializer.serialize(cityInfo));

        System.out.println(serializer.deserialize(connection.get(key.getBytes(StandardCharsets.UTF_8))));

        connection.flushAll();  // 慎用
    }

    @Test
    public void testUseRedisTemplate() {

        String key = "RedisTemplate";

        // -----------------------------------使用 RedisTemplate 存取 Java 对象-------------------------------------------

        CityInfo cityInfo = CityInfo.builder()
                .city("suzhou").longitude(116.58).latitude(33.38).build();

        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key, cityInfo);

        System.out.println(valueOperations.get(key));

        connection.flushAll();  // 慎用
    }

    @Test
    public void testUseStringRedisTemplate() {

        String key = "StringRedisTemplate";

        // -----------------------------------使用 StringRedisTemplate 存取 String 对象-----------------------------------

        CityInfo cityInfo = CityInfo.builder()
                .city("suzhou").longitude(116.58).latitude(33.38).build();

        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set(key, JSON.toJSONString(cityInfo));

        System.out.println(JSON.parseObject(valueOperations.get(key), CityInfo.class));

        connection.flushAll();  // 慎用
    }
}

总结

  • 当需要存取的数据是字符串类型时(大多数情况下),使用 StringRedisTemplate
  • 当需要存取的数据是 Java Object 时,使用 RedisTemplate(需要定义好序列化策略)
  • 不需要使用 RedisConnection

欢迎关注课程:

打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP