springboot2与redis整合

Redis,对于大家来说应该不陌生,是经常使用的开发技术之一。原始的RDBMS关系型数据库,能够存储海量的数据,但是在某些特殊的情况下,性能并不是特别的好,例如热点数据的读写,数据缓存等。Redis作为NoSQL中的一员,基于内存数据存储,支持多种数据结构,单线程处理,是一款高性能的可持久化key-value数据库。对于Redis就不做过多介绍了,相信大家或多或少都了解过,下面重点描述SpringBoot如何与Redis进行整合,若是大神,请一笑而过。

项目搭建

还是和之前一样,我们先来搭建项目,先学会如何使用,再来具体了解其中的内容和原理。

项目版本:SpringBoot (2.1.4)

首先,引入redis依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redis连接池依赖-->
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-pool2</artifactId>
   <version>2.6.1</version>
</dependency>

根据版本的不一致,引入方式可能不一样,若导入失败,请使用spring-boot-starter-redis。

application.properties:

#redis地址
spring.redis.host=192.168.159.129
#redis客户端lettuce配置
#最大连接数,负数表示无限制
spring.redis.lettuce.pool.max-active=8
#最大等待,负数表示无限制
spring.redis.lettuce.pool.max-wait=-1
#shutdown超时
spring.redis.lettuce.shutdown-timeout=100
#最大空闲连接数,负数表示无限制
spring.redis.lettuce.pool.max-idle=8
#最小空闲连接数
spring.redis.lettuce.pool.min-idle=0

更多配置信息,后文继续讲解。

启动类SpringbootRedisApplication:

@SpringBootApplication
@RestController
public class SpringbootRedisApplication {
    /**
     * 注入StringRedisTemplate
     */
    private final StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    public SpringbootRedisApplication(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    
    public static void main(String[] args) {
        SpringApplication.run(SpringbootRedisApplication.class, args);
    }
    
    @GetMapping("/name")
    public String  get(){
        this.stringRedisTemplate.opsForValue().set("name","英雄");
        return this.stringRedisTemplate.opsForValue().get("name");
    }
}

这是最简单的redis集成,大家可以发现我们没有创建RedisConnectionFactory、Pool等相关的XML或者注解信息,就能很好的供大家使用。其中的原理,相信大家已经了解,这是SpringBoot自动进行配置了,至于是如何配置的,后文继续。在这里,我先为大家介绍两个重要的类。

RedisTemplete&StringRedisTemplete

使用过Spring Data Redis的都知道,RedisTemplete和StringRedisTemplete是Spring Data为我们封装好的两种操作redis模板,使用它们,我们可以无需关注数据序列化和连接管理,使用户更加关注核心业务,减少connection 未close等问题。

RedisTemplete

RedisTemplete是操作redis命令的核心类,提供了丰富的API供大家使用,并且是线程安全的。我们可以发现,RedisTemplete提供了操作redis命令的Operations,大家都知道Redis支持多种数据类型,例如:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。RedisTemplete也针对redis的数据结构提供了多种类型的Operations。部分功能如下:

//键值对存储
this.redisTemplate.opsForValue().set("name","张三");
String name = (String)this.redisTemplate.opsForValue().get("name");
log.info(name);
//list存储
this.redisTemplate.opsForList().leftPushAll("names","李四","王五","赵六");
List names = this.redisTemplate.opsForList().range("names", 0, 3);
log.info(names.toString());
//hash使用
Map<String, String> map = new HashMap<>(4);
map.put("name","秦始皇");
map.put("age","未知");
map.put("sex","男");
this.redisTemplate.opsForHash().putAll("HASH_PERSON",map);
String s =(String) this.redisTemplate.opsForHash().get("HASH_PERSON", "name");
log.info(s);

当然,RedisTemplete的功能不仅仅如此,除了提供各种类型的Operations之外,还能通过它设置序列化方式,默认采用的是Jdk序列化方式JdkSerializationRedisSerializer,其它的功能需要大家慢慢探索,就不一 一展示了。在使用RedisTemplete的过程中需要注意,不支持键为null,不然会抛出java.lang.IllegalArgumentException: non null key required异常,value可以为null,在使用时键可以为空字符串,但是不建议大家这样使用。在使用的过程中,如果大家想要使用自定义或者其它序列化方式,可以自动注入RedisTemplete,例如:

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
    RedisTemplate redisTemplate = new RedisTemplate<>();
    //针对key设置StringRedisSerializer
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    //针对value设置Jackson2JsonRedisSerializer
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    return redisTemplate;
}

在项目中,key和value基本上都是String类型的数据,为了方便大家开发,又提供了StringRedisTemplete供大家使用。StringRedisTemplete是RedisTemplete的子类,采用StringRedisSerializer进行序列化。这里就不做过多介绍了,StringRedisTemplete只是RedisTemplete<String,String>的一个简单定义。

源码分析

上面的内容是使用SpringDataRedis模块的基础,众所周知,SpringBoot会自动配置Starter Poms依赖的相关内容,下面我们就来探讨探讨。

首先,我们需要找到redis的自动配置类。在使用SpringBoot的时候,会自动引入spring-boot-autoconfigure的依赖,从该依赖的spring.factories中,我们可以找到所有自动配置类的位置,搜索redis,找到自动配置类RedisAutoConfiguration。springboot2与redis整合

RedisAutoConfiguration.java:

需要RedisOperations类,该类指定了一系列Redis操作,由RedisTemplete实现
redis的配置从RedisProperties中获取
导入LettuceConnectionConfiguration、JedisConnectionConfiguration两种配置    对应Lettuce、jedis两种集成
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    /**
     * 若上下文不存在RedisTemplete Bean
     * 注入RedisTemplete Bean供开发使用
     */
   @Bean
   @ConditionalOnMissingBean(name = "redisTemplate")
   public RedisTemplate<Object, Object> redisTemplate(
         RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
      RedisTemplate<Object, Object> template = new RedisTemplate<>();
      template.setConnectionFactory(redisConnectionFactory);
      return template;
   }
    /**
     * 若上下文不存在StringRedisTemplate Bean
     * 注入StringRedisTemplate Bean供开发使用
     */
   @Bean
   @ConditionalOnMissingBean
   public StringRedisTemplate stringRedisTemplate(
         RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
      StringRedisTemplate template = new StringRedisTemplate();
      template.setConnectionFactory(redisConnectionFactory);
      return template;
   }
}

从中,我们发现Redis自动配置,支持使用Lettuce、jedis两种客户端操作。针对两种操作,SpringBoot提供了两套Redis连接配置LettuceConnectionConfiguration、JedisConnectionConfiguration。默认使用Lettuce客户端进行操作,若大家想要使用Jedis进行操作,也是可以的,如下:

        <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId>
             <exclusions>
             <!-- 去除 lettuce依赖-->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加Jedis客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

在进行自动配置时,Redis的配置信息按照如下方式获取:

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

   //默认数据库
   private int database = 0;
   //采用url进行配置,例如:redis://user:[email protected]:6379
   private String url;
   //redis服务的地址
   private String host = "localhost";
   //密码
   private String password;
   //端口
   private int port = 6379;
   //是否开启SSL
   private boolean ssl;
   //连接超时
   private Duration timeout;
   //哨兵模式配置
   private Sentinel sentinel;
   //集群模式配置
   private Cluster cluster;
   //支持Jedis配置
   private final Jedis jedis = new Jedis();
   //支持Lettuce配置
   private final Lettuce lettuce = new Lettuce();

   /**
    * 连接池配置
    */
   public static class Pool {
      //最大空闲连接,若为负数表示没有限制
      private int maxIdle = 8;
      //最小空闲连接
      private int minIdle = 0;
      //最大使用连接,若为负数表示没有限制
      private int maxActive = 8;
      //获取连接最大等待时间,-1代表永久等待
      private Duration maxWait = Duration.ofMillis(-1);
   }

   /**
    * 集群配置
    */
   public static class Cluster {
      //集群节点列表
      private List<String> nodes;
      //集群支持的最大重定向数
      private Integer maxRedirects;
   }

   /**
    * 哨兵模式配置
    */
   public static class Sentinel {
      // 哨兵服务名
      private String master;
      // 节点配置
      private List<String> nodes;
   }

   /**
    * jedis客户端配置
    */
   public static class Jedis {
      private Pool pool;
   }

   /**
    * Lettuce客户端配置
    */
   public static class Lettuce {
      private Duration shutdownTimeout = Duration.ofMillis(100);
      private Pool pool;
   }
}

LettuceConnectionConfiguration部分内容(Lettuce连接工厂):

/** 
 * 若上下文不存在RedisConnectionFactory Bean
 * 则进行实例化
 */
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(
      ClientResources clientResources) throws UnknownHostException {
   LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(
         clientResources, this.properties.getLettuce().getPool());
   return createLettuceConnectionFactory(clientConfig);
}

private LettuceConnectionFactory createLettuceConnectionFactory(
      LettuceClientConfiguration clientConfiguration) {
    //若为哨兵模式,使用哨兵模式创建连接工厂
   if (getSentinelConfig() != null) {
      return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
   }
    //若为集群模式,使用集群模式创建连接工厂
   if (getClusterConfiguration() != null) {
      return new LettuceConnectionFactory(getClusterConfiguration(),
            clientConfiguration);
   }
   //使用单节点模式创建连接工厂
   return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}

总结

本文主要讲述springboot与redis整合的基础内容以及redis的一些使用方式,大家可以自行编码测试相关内容。若有不对的地方,望大家指正,共同进步,谢谢。

源码:https://gitee.com/hpaw/SpringBoot2Demo/tree/master/springboot-redis