springboot整合redis

Jedis介绍

首先,我们知道redis其实是一个数据库,其并不能被java代码所控制。那么,为了能在java代码中操作redis数据库,那就必须要有一个连接层来控制redis。

如何使用jedis

jedis中的方法设计与原生redis命令几乎是一样的,因此,查询redis的命令手册就能够知道jedis中方法如何书写,那我们先看看一些基础命令。

// 通过jedis实例获取连接并操作redis
public class JedisExample {
 
    @Test
    public void testFirstExample() {
        // 连接redis
        Jedis jedis = new Jedis("localhost", 6379);
        // Jedis jedis = new Jedis("localhost"); // 默认6379端口
 
        // string类型
        jedis.set("name", "demo");
        String name = jedis.get("name");
 
        // list类型
        jedis.lpush("myList", "hello");
        jedis.rpush("myList", "world");
        String lpopVal = jedis.lpop("myList");
        String rpopVal = jedis.rpop("myList");
 
        // set类型
        jedis.sadd("mySet", "123");
        jedis.sadd("mySet", "456");
        jedis.sadd("mySet", "789");
        jedis.srem("mySet", "789");
        jedis.scard("mySet");
 
        // zset类型
        jedis.zadd("myZset", 99, "X");
        jedis.zadd("myZset", 90, "Y");
        jedis.zadd("myZset", 97, "Z");
        Double zscore = jedis.zscore("myZset", "Z");
 
        // 其他
        jedis.incr("intKey");
        jedis.incrBy("intKey", 5);
        jedis.del("intKey");
 
        // 触发持久化
        // jedis.save();
        // jedis.bgsave()
 
        // 关闭连接
        jedis.close();
    }
}
// 使用jedis数据库连接池技术,并获取jedis实例
public class JedisPoolExample {
 
    @Test
    public void testUsePool() {
 
        // 配置连接池
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);
 
        // 创建连接池
        JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
 
        Jedis jedis = jedisPool.getResource();
 
        // 使用jedis进行操作
        jedis.set("name", "otherNameVal");
 
        // 用完之后,一定要手动关闭连接(归还给连接池)
        jedis.close();
    }
}

jedis注意事项

jedis并不是一个线程安全的类,故不应该在多线程环境中共用一个Jedis实例。但是,也应该避免直接创建多个Jedis实例,因为这种做法会导致创建过多的socket连接(一个jedis就对应了一个连接),性能不高。 要保证线程安全且获得较好的性能,可以使用JedisPool。 JedisPool是一个连接池,既可以保证线程安全,又可以保证了较高的效率。

springboot如何整合redis

我们要学习springboot如何整合的,那就必须要先去spring的官网上查看相关说明,redis作为一种持久化技术,我们很容易的可以想到spring data整合,那么参考spring data就可以看到相应的说明。

这里使用Maven倒入springboot-redis的包,这里可以去仓库查询合适的版本号,我这里以springboot 2.1.9.RELEASE作为例子的版本

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.9.RELEASE</version>
</dependency>

查看springboot中redis的自动配置

image.png

下面就是该配置类中的内容

RedisAutoConfiguration.class

@Configuration
@ConditionalOnClass(RedisOperations.class)
// 这里通过读取RedisProperties类获取配置信息
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

  @Bean
      // 这个注解就是说在没有找打其他名为redisTemplate的类就使用该默认配置
      // 这里返回一个redisTemplate,通过该redisTemplate就可以操作redis
  @ConditionalOnMissingBean(name = "redisTemplate")
  public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
          throws UnknownHostException {
      RedisTemplate<Object, Object> template = new RedisTemplate<>();
              // 这里设置了连接工厂,通过定义不同的工厂可以用不同的组件连接redis
              // springboot默认配置了两种连接手段
              // 1. jedis(springboot 1.X)
              // 2. lettuce(springboot 2.X)
      template.setConnectionFactory(redisConnectionFactory);
      return template;
  }
      
      // 该类是单独为String设计的RedisTemplate
  @Bean
  @ConditionalOnMissingBean
  public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
          throws UnknownHostException {
      StringRedisTemplate template = new StringRedisTemplate();
      template.setConnectionFactory(redisConnectionFactory);
      return template;
  }

}

RedisConnectionFactory是一个接口,其中提供了两个实现

public interface RedisConnectionFactory extends PersistenceExceptionTranslator {

    /**
     * Provides a suitable connection for interacting with Redis.
     *
     * @return connection for interacting with Redis.
     */
    RedisConnection getConnection();

    /**
     * Provides a suitable connection for interacting with Redis Cluster.
     *
     * @return
     * @since 1.7
     */
    RedisClusterConnection getClusterConnection();

    /**
     * Specifies if pipelined results should be converted to the expected data type. If false, results of
     * {@link RedisConnection#closePipeline()} and {RedisConnection#exec()} will be of the type returned by the underlying
     * driver This method is mostly for backwards compatibility with 1.0. It is generally always a good idea to allow
     * results to be converted and deserialized. In fact, this is now the default behavior.
     *
     * @return Whether or not to convert pipeline and tx results
     */
    boolean getConvertPipelineAndTxResults();

    /**
     * Provides a suitable connection for interacting with Redis Sentinel.
     *
     * @return connection for interacting with Redis Sentinel.
     * @since 1.4
     */
    RedisSentinelConnection getSentinelConnection();
}
image.png

RedisProperites.class提供了配置的相关信息

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

    /**
     * Database index used by the connection factory.
     */
    private int database = 0;

    /**
     * Connection URL. Overrides host, port, and password. User is ignored. Example:
     * redis://user:password@example.com:6379
     */
    private String url;

    /**
     * Redis server host.
     */
    private String host = "localhost";

    /**
     * Login password of the redis server.
     */
    private String password;

    /**
     * Redis server port.
     */
    private int port = 6379;

    /**
     * Whether to enable SSL support.
     */
    private boolean ssl;

    /**
     * Connection timeout.
     */
    private Duration timeout;

    private Sentinel sentinel;

    private Cluster cluster;

    private final Jedis jedis = new Jedis();

    private final Lettuce lettuce = new Lettuce();

        // 省略了get & set方法
}

RedisTemplate有何用

好了,我们知道我们配置的最终目的都是在生成这个RedisTemplate,那么这个东西到底有什么用呢?

其实,这个东西就是spring给我们提供的一种封装。我们应该还学过一种类似的——JDBCTemplate,使用该类来操作数据库。那么同样,spring也提供了redisTemplate用于在springboot中操作redis。进而避免直接使用jedis或者lettuce,通过再加一层的方式屏蔽组件的差异。

如何自定义自己的RedisTemplate

好了,既然知道redisTemplate是什么,我们就要学会如何定制他,以让他满足我们的使用。

首先,看看这个东西里面到底是什么?

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

    private boolean enableTransactionSupport = false;
    private boolean exposeConnection = false;
    private boolean initialized = false;
    private boolean enableDefaultSerializer = true;
    private @Nullable RedisSerializer<?> defaultSerializer;
    private @Nullable ClassLoader classLoader;

    @SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
    @SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
    @SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
    @SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
    private RedisSerializer<String> stringSerializer = RedisSerializer.string();

    private @Nullable ScriptExecutor<K> scriptExecutor;

    private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this);
    private final ListOperations<K, V> listOps = new DefaultListOperations<>(this);
    private final SetOperations<K, V> setOps = new DefaultSetOperations<>(this);
    private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations<>(this);
    private final GeoOperations<K, V> geoOps = new DefaultGeoOperations<>(this);
    private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations<>(this);
    private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations<>(this);
}

因为实在太长了,这里就没有列出方法,而是列出了其中的一些属性。我们知道方法大多数都是用于操作redis的,而真正重要的应该是其中的属性

我们可以看到,这个类中大多数都在描述一个东西——Serializer,即序列化。 为什么要讨论这个东西?

redis并不是java,并不能存储对象这种东西,因此,最后底层一个java对象要被保存就必须通过序列化的技术。

那么springboot到底提供了几种序列化方式呢?

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352