【项目demo06】SpringBoot整合Redis

我准备开一个系列,就是写一些在简要的学习项目中可能会用到的奇奇怪怪的功能,比如线程池或者统一异常处理类
取名为【项目demo】系列
然后将会同步到GitHub中:https://github.com/Livorth/FunctionalLearning


SpringBoot整合Redis

Redis是目前使用最多的非关系型数据库,如果要详细学的话,都可以开一个新系列了

我的这篇博客构建的基础是在你大致掌握了Redis之上来写的

虽然是这么说,我自己其实对redis的了解也不是很深刻,所以暂时仅了解如何使用

我的主要参考来源:


添加依赖与yaml配置

不管怎么样使用什么新的东西总还是要添加配置与对应配置,然后再是自定义配置与使用

  1. 添加依赖

    <!--    组合redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 使用lettuce做redis的连接池需要额外引入这个包-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    

    如果你不需要连接池,那么不用加上lettuce

  2. 修改对应的yaml配置

      redis:
        # 地址
        host: localhost
        # 端口号
        port: 6379
        # 密码
        password: ***
        # 超时时间,单位毫秒
        timeout: 3000
        # 数据库编号
        database: 0
        # 配置lettuce
        lettuce:
          pool:
            # 连接池中的最小空闲连接
            min-idle: 1
            # 连接池中的最大空闲连接
            max-idle: 6
            # 连接池最大连接数(使用负值表示没有限制,不要配置过大,否则可能会影响redis的性能)
            max-active: 10
            # 连接池最大阻塞等待时间(使用负值表示没有限制);单位毫秒
            max-wait: 1000
          #关闭超时时间;单位毫秒
          shutdown-timeout: 200
    

    不过实际上要说的话,其实只需要host、port、password 就可以开始使用了,其他的配置只是列出来,要配的时候知道有就好了

使用已有的RedisTemplate

springboot的redis自动装配类已经配置了一个StringRedisTemplate的bean

但是上述bean有限制条件,那就是key与value都是String,不过也可以直接使用RedisTemplate,那就是<key, value> => <Object, Object>,更不方便使用

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

public class StringRedisTemplate extends RedisTemplate<String, String>{}

当需要拓展功能的话就只能自己写RedisTemplate了,这个在后续会提及

这里仅简单测试一下其是否能正常连接redis,以及一般<key, value> => <String, String>的使用

package cn.livorth.functionallearning;

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.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

@SpringBootTest
class FunctionalLearningApplicationTests {

    @Autowired
    private StringRedisTemplate strRedis;

    @Test
    void redisTestStringSet(){
        strRedis.opsForValue().set("RedisTest", "Test");
        strRedis.opsForValue().set("RedisTest中文", "中文测试");
    }

    @Test
    void redisTestStringGet(){
        String redisTest = strRedis.opsForValue().get("RedisTest");
        System.out.println(redisTest);
    }
}

在这里插入图片描述在这里插入图片描述

像这种key与value都是String的使用情况,其实可以不用再自己重写RedisTemplate,作为简单的使用还是不错的选择

自定义配置RedisTemplate

在我参考的博客中,主要有两种不同复杂度的配置方案

  1. 方案一:更普遍,更简单

    这里参考博客:https://zhuanlan.zhihu.com/p/139528556

    @Configuration
    public class RedisConfig {
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
            // 创建redisTemplate
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(connectionFactory);
            // 使用Jackson2JsonRedisSerialize替换默认序列化
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
            // key采用String的序列化方式
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            // value序列化方式采用jackson
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            // hash的key也采用String的序列化方式
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            // hash的value序列化方式采用jackson
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
  2. 方案二:结合连接池,为后续的redis集群做准备,相对复杂

    这里参考博客:https://www.cnblogs.com/vchar/p/14591566.html

    package cn.livorth.functionallearning.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.cache.RedisCacheWriter;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.util.DigestUtils;
    
    import java.time.Duration;
    
    /**
     * @program: FunctionalLearning
     * @description: redis配置类
     * @author: livorth
     * @create: 2021-10-05 16:21
     **/
    @Configuration
    public class RedisConfig<K, V> extends CachingConfigurerSupport {
    
        /**
         * 自定义缓存注解key的生成策略。默认的生成策略是看不懂的(乱码内容)
         * 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
         * 这里是生成的key是:类全名.方法名 方法参数(的md5加密)
         */
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return (target, method, params) -> {
                StringBuilder prefix = new StringBuilder();
                prefix.append(target.getClass().getName());
                prefix.append(".").append(method.getName());
                StringBuilder sb = new StringBuilder();
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return prefix.append(DigestUtils.md5DigestAsHex(sb.toString().getBytes()));
            };
        }
    
        /**
         * 缓存配置管理器
         */
        @Bean
        public CacheManager cacheManager(LettuceConnectionFactory factory) {
            // 以锁写入的方式创建RedisCacheWriter对象
            RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
            // 设置缓存注解的缓存时间,缓存1小时
            Duration duration = Duration.ofSeconds(3600L);
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(duration);
            return new RedisCacheManager(writer, redisCacheConfiguration);
        }
    
        /**
         * 修改redisTemplate的序列化方式
         * @param factory LettuceConnectionFactory,即连接池的链接方式
         */
        @Bean(name = "redisTemplate")
        public RedisTemplate<K, V> redisTemplate(LettuceConnectionFactory factory) {
            //创建RedisTemplate对象
            RedisTemplate<K, V> template = new RedisTemplate<K, V>();
            template.setConnectionFactory(factory);
            //设置key的序列化方式
            template.setKeySerializer(keySerializer());
            template.setHashKeySerializer(keySerializer());
    
            //设置RedisTemplate的Value序列化方式Jackson2JsonRedisSerializer;默认是JdkSerializationRedisSerializer
            template.setValueSerializer(valueSerializer());
            template.setHashValueSerializer(valueSerializer());
    
            template.afterPropertiesSet();
            return template;
        }
    
        /**
         * key采用String的序列化方式
         * @return
         */
        private RedisSerializer<String> keySerializer() {
            return new StringRedisSerializer();
        }
    
        /**
         * value序列化方式采用jackson
         * @return
         */
        private RedisSerializer<Object> valueSerializer() {
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
    
            ObjectMapper om = new ObjectMapper();
            // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
            om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
            //解决时间序列化问题
            om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            om.registerModule(new JavaTimeModule());
    
            jackson2JsonRedisSerializer.setObjectMapper(om);
            return jackson2JsonRedisSerializer;
        }
    
    }
    

其实这两种方案实际上的配置是一样的,区别在于使用的 ConnectionFactory 不同,这里不做深入讨论,建议自行百度了解

做这一步的配置主要原因是修改redisTemplate的序列化方式,redisTemplate原本的序列方式是JdkSerializationRedisSerializer,这里我们修改为Jackson2JsonRedisSerializer,也就是可以将传入的对象格式化为JSON。

简单测试

写两个接口操作redis

UserController.java

@GetMapping("setInCache/{id}")
public Boolean setAllUserInCacheById(@PathVariable("id") int id){
    return userService.setAllUserInCacheById(id);
}

@GetMapping("getInCache/{id}")
public User getAllUserInCacheById(@PathVariable("id") int id){
    return userService.getAllUserByCacheById(id);
}

UserServiceImpl.java

@Override
public Boolean setAllUserInCacheById(int id) {
    User user = userMapper.selectById(id);
    redisTemplate.opsForValue().set("UserInfo:" + id, user);
    return true;
}

@Override
public User getAllUserByCacheById(int id) {
    User user = (User) redisTemplate.opsForValue().get("UserInfo:" + id);
    return user;
}

然后分别访问

在这里插入图片描述
啊
上传和获取都很成功

封装工具类

这里我采用的java redisUtils工具类很全中封装的工具类,太长了就不在复制粘贴了

不过这种工具类得按照自己的需求来,很多东西得自己改或者自己写,随机应变吧

当你的项目使用redis操作较多而且比较复杂的时候确实可以封装个工具类,但是只是随便搞点String-String缓存我觉得甚至都没必要自定义配置RedisTemplate


写在后面

最开始我以为Redis的整合很简单,但是当真正了解之后才知道自己目前知道的东西只是冰山一角,但是受篇幅限制这里只能简单介绍如何在SpringBoot中使用Redis,实际上还有很多内容可以单独拎出来说,比如

  • Redis集群连接配置以及读写分离:https://www.cnblogs.com/vchar/p/14591566.html

  • 高并发下穿透问题处理:https://www.cnblogs.com/shenlailai/p/10583869.html

  • 数据库同步

还是以后时间足够再深入了解吧


我的项目demo系列都会同步到GitHub上,欢迎围观

https://github.com/Livorth/FunctionalLearning

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值