Redis的Java客户端

发布于:2025-09-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

Jedis客户端

快速入门

引入依赖:

建立连接

测试

释放资源

完整代码

连接池

SpringDataRedis客户端

快速入门

引入依赖

配置Redis

注入RedisTemplate

编写测试

测试结果

自定义序列化

StringRedisTemplate


在Redis官网中提供了各种语言的客户端,地址:Redis客户端下载https://www.redis.net.cn/clients/

其中Java客户端也包含很多:

标记为*的就是推荐使用的java客户端,包括:

  • Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。

  • Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。

Jedis客户端

Jedis的官网地址: https://github.com/redis/jedis

快速入门

我们先来个快速入门,首先创建一个maven项目:

引入依赖:

<!--jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>
<!--单元测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

建立连接

新建一个单元测试类,内容如下:

	private Jedis jedis;

	@BeforeEach
	void setUp() {
		//1.建立连接
		jedis = new Jedis("192.168.200.130", 6379);
		//2.设置密码
		jedis.auth("123456");
		//3.选择数据库
		jedis.select(0);
	}

测试

	@Test
	void testString() {
		//1.添加
		String result = jedis.set("name", "tom");
		System.out.println(result);
		//2.查询
		String name = jedis.get("name");
		System.out.println(name);
	}

	@Test
	void testHash() {
		//添加
		jedis.hset("user:10001", "name", "tashuo");
		jedis.hset("user:10001", "age", "21");
		//查询
		Map<String, String> map = jedis.hgetAll("user:10001");
		System.out.println(map);
	}

释放资源

	@AfterEach
	void tearDown() {
		if(jedis != null) {
			jedis.close();
		}
	}

完整代码

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

import java.util.Map;

public class JedisTest {
	private Jedis jedis;

	@BeforeEach
	void setUp() {
		//1.建立连接
		jedis = new Jedis("192.168.200.130", 6379);
		//2.设置密码
		jedis.auth("123456");
		//3.选择数据库
		jedis.select(0);
	}

	@Test
	void testString() {
		//1.添加
		String result = jedis.set("name", "tom");
		System.out.println(result);
		//2.查询
		String name = jedis.get("name");
		System.out.println(name);
	}

	@Test
	void testHash() {
		//添加
		jedis.hset("user:10001", "name", "tashuo");
		jedis.hset("user:10001", "age", "21");
		//查询
		Map<String, String> map = jedis.hgetAll("user:10001");
		System.out.println(map);
	}

	@AfterEach
	void tearDown() {
		if(jedis != null) {
			jedis.close();
		}
	}
}

连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。

import redis.clients.jedis.*;

public class JedisConnectionFactory {

    private static JedisPool jedisPool;

    static {
        // 配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(8);// 最大连接数
        poolConfig.setMaxIdle(8);// 最大空闲连接数
        poolConfig.setMinIdle(0);// 最小空闲连接数
        poolConfig.setMaxWaitMillis(1000);// 最大等待时间
        // 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
        jedisPool = new JedisPool(poolConfig, "192.168.200.130", 6379, 1000, "123456");
    }

    // 获取Jedis对象
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

然后,我们需要对刚刚的测试代码进行修改,修改为通过连接池获取对象的写法:

首先,就是修改获取Jedis对象的方式,由构造方法获取改为通过连接池获取:

	@BeforeEach
	void setUp() {
		//1.建立连接
		//通过构造方法创建Jedis对象
		//jedis = new Jedis("192.168.200.130", 6379);
		//通过静态方法创建Jedis对象(通过连接池获取对象)
		jedis = JedisConnectionFactory.getJedis();
		//2.设置密码
		jedis.auth("123456");
		//3.选择数据库
		jedis.select(0);
	}

获取的Jedis对象不再需要销毁,而是使用完之后归还到连接池中,

	@AfterEach
	void tearDown() {
		if(jedis != null) {
			jedis.close();
		}
	}

代码还是那段代码,只是底层方法会判断你这个Jedis对象是通过构造方法创建的还是从连接池获取的:如果是根据构造方法生成的,会进行销毁;如果是从连接池获取的,使用完之后会归还到连接池中。底层方法如下:

    public void close() {
        if (this.dataSource != null) {
            JedisPoolAbstract pool = this.dataSource;
            this.dataSource = null;
            if (this.isBroken()) {
                pool.returnBrokenResource(this);
            } else {
                pool.returnResource(this);
            }
        } else {
            super.close();
        }

    }

测试,查看效果,可知通过连接池获取Jedis对象的操作成功:

SpringDataRedis客户端

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:Spring Data Redis

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)

  • 提供了RedisTemplate统一API来操作Redis

  • 支持Redis的发布订阅模型

  • 支持Redis哨兵和Redis集群

  • 支持基于Lettuce的响应式编程

  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化

  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:

快速入门

SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单。

首先,新建一个maven项目,然后按照下面步骤执行:

引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.heima</groupId>
    <artifactId>redis-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--common-pool-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--Jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
配置Redis
spring:
  data:
    redis:
      host: 192.168.200.130
      port: 6379
      password: 123456
      database: 0
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: 1000ms
注入RedisTemplate

因为有了SpringBoot的自动装配,我们可以拿来就用:

	@Resource
	private RedisTemplate redisTemplate;
编写测试
	@Test
	void testString() {
		// 设置值
		redisTemplate.opsForValue().set("name","她说");
		// 获取值
		Object name = redisTemplate.opsForValue().get("name");
		System.out.println("name="+name);
	}
测试结果

自定义序列化

刚刚的操作,我们是以name为她说进行存储的,但是从下图种可以看出,键和值都变成了一串字符,那么为什么会存在这种情况呢?

这是因为 Spring Data Redis 在默认情况下使用了 Java 序列化机制 来处理对象的存储,导致中文键和值被序列化为了字节流的十六进制表示。

Spring Data Redis 的默认序列化器是 JdkSerializationRedisSerializer,它会:

  • 将对象(包括键和值)通过 Java 自带的序列化机制转换成字节数组

  • 中文在序列化后会变成对应的字节编码(以 \x 开头的十六进制形式)
  • 你看到的 \xAC\xED\x00\x05 是 Java 序列化的魔术头(固定标识),后面的是实际内容的字节表示

这是由SpringDataRedis客户端的默认序列化器 JdkSerializationRedisSerializer造成的,要让我们的键值对显示中文,我们就需要修改其默认的序列化器。

我们可以自定义RedisTemplate的序列化方式,代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

@Configuration
public class RedisConfig {

	@Bean
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
		// 创建RedisTemplate对象
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		// 设置连接工厂
		template.setConnectionFactory(connectionFactory);
		// 创建JSON序列化工具
		GenericJackson2JsonRedisSerializer jsonRedisSerializer =
				new GenericJackson2JsonRedisSerializer();
		// 设置Key的序列化
		template.setKeySerializer(RedisSerializer.string());
		template.setHashKeySerializer(RedisSerializer.string());
		// 设置Value的序列化
		template.setValueSerializer(jsonRedisSerializer);
		template.setHashValueSerializer(jsonRedisSerializer);
		// 返回
		return template;
	}
}

然后,就需要对我们的代码进行修改了:

上面的代码表示,键是String类型的,值是任意的;

除此之外,因为当前项目不是SpringMVC的,所以需要我们主动导入依赖来执行序列化操作,在集成了SpringMVC的项目中,这个依赖是SpringMVC底层自带的:

        <!--Jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>

然后我们重新运行测试:

从IDEA输出和图形化界面根据可知,键和值一切正常。

再来测试一下,Java对象能不能正常存储,首先创建一个Java对象:

(不知道为何,这里使用Lombok注解总是提示参数个数不一致,所以此处用JavaBean)

public class User {
	private String name;
	private Integer age;


	public User() {
	}

	public User(String name, Integer age) {
		this.name = name;
		this.age = age;
	}

	/**
	 * 获取
	 * @return name
	 */
	public String getName() {
		return name;
	}

	/**
	 * 设置
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 获取
	 * @return age
	 */
	public Integer getAge() {
		return age;
	}

	/**
	 * 设置
	 * @param age
	 */
	public void setAge(Integer age) {
		this.age = age;
	}

	public String toString() {
		return "User{name = " + name + ", age = " + age + "}";
	}
}

启动测试,查看控制台输出:

查看Redis图形化界面根据查看结果:

整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

StringRedisTemplate

自定义序列化的方式,虽然也能解决Java对象自动序列化为JSON字符串,以及在查询时能够自动把JSON反序列化为Java对象,但是这张方式记录了序列化时对应的class名称,这样带来额外的内存开销。有时候,我们要存储的对象可能还没有class名称所占的内存大,这种情况这样的方式就显得不那么友好了。

为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。

因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。源码如下:

省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用:

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	// JSON序列化工具
	private static final ObjectMapper mapper = new ObjectMapper();

	@Test
	void testString() {
		// 设置值
		stringRedisTemplate.opsForValue().set("name","晴天");
		// 获取值
		Object name = stringRedisTemplate.opsForValue().get("name");
		System.out.println("name="+name);
	}

Redis可视化工具显示为如下图,这说明字符串类型可以直接存储:

当我们需要存储Java对象时,就需要手动序列化和反序列化了,代码如下:

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	// JSON序列化工具
	private static final ObjectMapper mapper = new ObjectMapper();

    @Test
	void testSaveUser() throws JsonProcessingException {
		// 创建对象
		User user = new User("她说", 21);
		// 手动序列化
		String json = mapper.writeValueAsString(user);
		// 写入数据
		stringRedisTemplate.opsForValue().set("user:200", json);

		// 获取数据
		String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
		// 手动反序列化
		User user1 = mapper.readValue(jsonUser, User.class);
		System.out.println("user1 = " + user1);
	}

可知,这张方式就比自定义序列化多了手动序列化和反序列化的过程,这个过程是由代码来实现的,在Redis上也是以JSON的格式存储的,自然就不会记录序列化时对应的class名称,这种方式也是目前所采用最多的方式,因为其友好性。Redis可视化工具显示如下:

可知,未记录序列化时对应的class名称,看起来清爽不少。

上述两种方式的优缺点如下,没有绝对的好坏,但是第二种更常用,推荐使用:


网站公告

今日签到

点亮在社区的每一天
去签到