SpringBoot整合Redis(自动配置分析、切换Jedis、自定义序列化器)

本文详细介绍了如何在SpringBoot项目中集成Redis,包括添加依赖、自动配置分析、配置连接参数、使用Jedis和Lettuce客户端、自定义序列化器以及处理序列化问题,展示了如何测试和解决常见问题。
摘要由CSDN通过智能技术生成

1. Redis介绍

Redis是一个开源(BSD许可)的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)与范围查询、bitmaps、hyperloglogs和地理空间(geospatial)索引半径查询。 Redis内置了复制(replication)、LUA脚本(Lua scripting)、LRU驱动事件(LRU eviction)、事务(transactions)和不同级别的磁盘持久化(persistence), 并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)

2. 添加pom.xml依赖

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

可以看到,会自动添加spring-data-redis和lettuce(netty实现)的redis客户端
spring-boot-starter-data-redis

3. 自动配置分析

查看spring-boot-autoconfigure-2.7.5.jar,,可以看到:

  • 绑定了RedisProperties配置类
  • 向IOC容器添加了LettuceConnectionConfiguration和JedisConnectionConfiguration这两个redis客户端的连接配置组件
  • 向IOC容器添加了RedisTemplate(key和value都是Object)和StringRedisTemplate(key和value都是String),用来操作redis
package org.springframework.boot.autoconfigure.data.redis;
.....省略部分......
@AutoConfiguration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {

    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

查看RedisProperties配置类,redis的配置由spring.redis开头的配置进行配置的

.....省略部分......
@ConfigurationProperties(
    prefix = "spring.redis"
)
public class RedisProperties {
.....省略部分......
}

我们再来看LettuceConnectionConfiguration这个配置类,可以看到

  • LettuceConnectionConfiguration类添加到IOC容器的条件是:我们的的配置参数没有指定spring.redis.client-type,或配置参数spring.redis.client-type=lettuce
  • 会向IOC容器添加LettuceConnectionFactory组件,其继承了RedisConnectionFactory,可以从该Factory获取到redis客户端的连接
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisClient.class})
@ConditionalOnProperty(
    name = {"spring.redis.client-type"},
    havingValue = "lettuce",
    matchIfMissing = true
)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
.....省略部分......
    @Bean
    @ConditionalOnMissingBean({RedisConnectionFactory.class})
    LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) {
        LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
        return this.createLettuceConnectionFactory(clientConfig);
    }
.....省略部分......
}

我们再来看JedisConnectionConfiguration这个配置类,可以看到

  • JedisConnectionConfiguration类添加到IOC容器的条件是:
    1. pom.xml中添加了Jedis相关的依赖
    2. 我们的的配置参数指定了spring.redis.client-type=jedis
  • 会向IOC容器添加JedisConnectionFactory组件,其继承了RedisConnectionFactory,可以从该Factory获取到redis客户端的连接
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class})
@ConditionalOnMissingBean({RedisConnectionFactory.class})
@ConditionalOnProperty(
    name = {"spring.redis.client-type"},
    havingValue = "jedis",
    matchIfMissing = true
)
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
......省略部分......
    @Bean
    JedisConnectionFactory redisConnectionFactory(ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
        return this.createJedisConnectionFactory(builderCustomizers);
    }
......省略部分......
}

4. application.properties配置

说明:

  • 可以通过url指定连接信息,也可以单独指定各个连接信息
  • 明确使用lettuce连接池
  • 指定了连接池同时活跃的最大连接数,和最小空闲连接数
# 格式:redis://username:password@IP:Port
spring.redis.url=redis://default:redis123@192.168.28.12:6379

# 单独指定redis各配置参数
# spring.redis.username=default
# spring.redis.password=redis123
# spring.redis.host=192.168.28.12
# spring.redis.port=6379

spring.redis.client-type=lettuce

# 连接池同时活跃的最大连接数
spring.redis.lettuce.pool.max-active=10
# 连接池最小空闲连接数
spring.redis.lettuce.pool.min-idle=5

5. 连接Redis测试

下面做了如下操作:

  • 先对一个key设置了一个值,再进行值的获取
  • 对一个key进行increment增量加1
  • 查看当前的redis连接池Class
package com.hh.springboottest;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@Slf4j
@SpringBootTest
public class MyApplicationTest {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Test
    public void queryDataTest() {
	   
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        operations.set("redis-key", "hello world");
        String redisKey = operations.get("redis-key");
        log.info("获取到的redis-key: {}", redisKey);

        // 对redis的key的value进行加1
        redisTemplate.opsForValue().increment("increment-key");

        log.info("当前使用的redis客户端是: {}", redisConnectionFactory.getConnection());
    }
}

运行程序,结果如下:

2022-11-20 07:32:05.096  INFO 12476 --- [           main] com.hh.springboottest.MyApplicationTest  : 获取到的redis-key: hello world
2022-11-20 07:32:05.101  INFO 12476 --- [           main] com.hh.springboottest.MyApplicationTest  : 当前使用的redis客户端是: org.springframework.data.redis.connection.lettuce.LettuceConnection@57466fb7
2022-11-20 07:32:05.288  INFO 12476 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2022-11-20 07:32:05.296  INFO 12476 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed

6. 使用Jedis客户端

在pom.xml添加依赖

        <!-- spring支持的jedis版本是3.8.0。使用当前最新的4.3.1版本会报错,可能不支持 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

然后修改application.properties参数文件。指定spring.redis.client-type=jedis。如下所示:

spring.redis.client-type=jedis
spring.redis.jedis.pool.max-active=10

7. 自定义默认的序列化器

默认使用JdkSerializationRedisSerializer序列化器,使用RedisTemplate保存User对象,在Redis中看到的是乱码,而且其它类型的框架也不能正确读取这个User对象。所以可以将User对象以json的方式进行保存

如果没有RedisTemplate,SpringBoot会自动添加一个默认的RedisTemplate。所以我们可以自己给IOC容器添加我们自定义的RedisTemplate,使用GenericJackson2JsonRedisSerializer序列化器,允许Object类型的key和value,都可以被转为json进行存储。具体实现如下:

package com.hh.springboottest.config;

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;


@Configuration
public class RedisConfiguration {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory /*会自动注入进来*/) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }

}

8. 序列化问题

  1. LocalDateTime序列化问题
    对于java.time.LocalDateTime,保存到redis的是时间戳,从redis读取出来的也是时间戳。需要对保存的字段进行格式化。如下所示。这样保存和读取的就是指定的格式
  // import com.fasterxml.jackson.annotation.JsonFormat;
  @JsonFormat( pattern="yyyy-MM-dd HH:mm:ss")
  private LocalDateTime updateTime;
  1. Map序列化不包含null值问题
    对于实体类的字段类型为java.util.Map,如果map中的value为null,则该key和value不会写入到redis,可以在属性上添加fastjson2的注解进行解决,如下所示
  // import com.alibaba.fastjson2.JSONWriter;
  // import com.alibaba.fastjson2.annotation.JSONField;
  @JSONField(serializeFeatures = JSONWriter.Feature.WriteMapNullValue)
  private Map<Long,String> idNameMap;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值