SpringBoot 整合 Redis(使用自定义 RedisTemplate)

八 SpringBoot 整合 Redis

(一) 简单使用(存在序列化问题)

① 创建 Springboot项目或模块,引入依赖

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

可以看到,这个官方的 starter 引入了 Redis,但是并没有引入 Jedis,而是引入了 Lettuce

Jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式

Lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式

② 编写配置文件

# 配置redis
spring.redis.host=192.168.122.1
spring.redis.port=6379

③ 测试代码

@SpringBootTest
class Redis02BootApplicationTests {
    
    

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
    
    
        redisTemplate.opsForValue().set("name","zhangsan");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }
}

运行结果:

zhangsan

(二) 使用自定义 RedisTemplate 模板(推荐)

上述操作,在 IDEA 中的结果肯定是没问题的,但是我们去 Linux 中去看一下 Redis 的内容,却发现 key 都是乱码,例如存储的 name 却变成了如下内容

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"

这就是序列化的问题,下面我们会分析这个问题,这里先给出解决方案,即自定义 RedisTemplate 模板

① 自定义 RedisConfig 类

@Configuration
public class RedisConfig {
    
    

    /**
     * 自定义 RedisTemplate 怒ban
     *
     * @param factory
     * @return
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    
    
        // 为开发方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

② 调用

@SpringBootTest
class Redis02BootApplicationTests {
    
    

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
    
    
        redisTemplate.opsForValue().set("address","beijing");
        System.out.println(redisTemplate.opsForValue().get("address"));
    }
}

我们分别存储了 name2 和 address 这两个key,去终端中查看一下

127.0.0.1:6379> keys *
1) "address"
2) "\xac\xed\x00\x05t\x00\x04name"
3) "name2"

可以看到,问题被解决了

(三) 封装一个工具类

我们操作追求的是便捷,而每次都是用一大堆 redisTemplate.opsForValue().xxxxx 很长的命令,所以封装一个工具类能更加事半功倍,具体工具类我就不在这里贴了,因为太长了,后面我会传到 github上去然后更新链接,当然了百度上其实一搜一大把

例如下面,我们使用工具类后就可以很简单的使用封装 redisTemplate 后的方法了

    @Autowired
    private RedisUtil redisUtil;

    @Test
    void contextLoads() {
    
    
        redisUtil.set("address2", "zhuhai");
        System.out.println(redisUtil.get("address2"));

    }

(四) 简单分析原理

这一块的简单分析,主要想要弄清楚四个内容

  • ① 是不是不再使用 Jedis 了,而是使用 Lettuce
  • ② 查看配置文件的配置属性
  • ③ 如何操作 Redis
  • ④ 序列化问题(即存储乱码问题)

在以前 Springboot 的文章中,关于自动配置的原理中可知,整合一个内容的时候,都会有一个自动配置类,然后再 spring.factories 中能找到它的完全限定类名

进入 spring.factories,查找关于 redis 的自动装配类

进入 RedisAutoConfiguration 类后,在注解中就能看到 RedisProperties 类的存在,这很显然是关于配置文件的类

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
// 这个注解!!!
@EnableConfigurationProperties(RedisProperties.class)
@Import({
    
     LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    
    // ...... 省略
}

进入 RedisProperties 类

alt + 7 可以在 IDEA 中查看到这个类中的属性方法等,这里查看一下其属性

例如地址端口,超时时间等等配置都清楚了,例如我们当时的配置文件是这样配的

# 配置redis
spring.redis.host=192.168.122.1
spring.redis.port=6379

继续回到类,查看下面一个注解,发现有两个类

  • LettuceConnectionConfiguration

  • JedisConnectionConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
// 这个注解!!!
@Import({
    
     LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    
    // ...... 省略
}

先来进入 Jedis 这个看看,GenericObjectPool 和 Jedis 这两个内容都是默认缺失的,所以是不会生效的

再来看看 LettuceConnectionConfiguration,是没有问题的,所以确实现在默认的实现都是使用 Lettuce 了

继续回到 RedisAutoConfiguration

其 Bean 只有两个

  • RedisTemplate
  • StringRedisTemplate

这种 XxxTemplate ,例如 JdbcTemplate、RestTemplate 等等都是通过 Template 来操作这些组件,所以这里的两个 Template 也是这也昂,分别用来操作 Redis 和 Redis 的 String 数据类型(因为 String 类型很常用)

// 注解略
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;
	}
}

特别注意这一个注解

@ConditionalOnMissingBean(name = "redisTemplate")

它的意思就是说如果没有自定义,就默认使用这个,这也就是告诉我们,我们可以自己自定义Template,来覆盖掉默认的,前面的使用中我们知道,使用默认的 Template 会涉及到乱码,也就是序列化问题

因为在网络中传输的对象需要序列化,否则就是乱码

我们进入默认的 RedisTemplate 看看

首先看到的就是一些关于序列化的参数

往下看,可以看到默认的序列化方式使用的是 Jdk 序列化,而我们自定义中使用的是 Json 序列化

而默认的RedisTemplate中的所有序列化器都是使用这个序列化器

RedisSerializer 中为我们提供了多种序列化方式

所以后来我们就自定了 RedisTemplate 模板,重新定义了各种类型的序列化方式,这也是我们推荐的做法

猜你喜欢

转载自blog.csdn.net/weixin_44652781/article/details/113104115