Redis基础

课程内容

  • Redis入门
  • Redis数据类型
  • Redis常用命令
  • 在Java中操作Redis

    1. 前言

    1.1 什么是Redis

    redis是一款非关系型数据库,redis存储的数据是在内存中的。特点:可以快速读取到数据,因为存储的数据是在内存中的。Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。

  • 基于内存存储,读写性能高(运行内存)

  • 适合存储热点数据(热点商品、资讯、新闻),一般数据一般变化频率比较低,或者是高并发请求数据
  • 企业应用广泛

* Redis的使用 - 图1

1.2 使用Redis能做什么

  • 数据缓存
  • 消息队列
  • 注册中心
  • 发布订阅

    2. Redis入门

    2.1 Redis简介

    Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. 翻译为:Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。
    官网:https://redis.io
    Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的NoSql数据库。
    NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充。
    关系型数据库(RDBMS):

  • Mysql

  • Oracle
  • DB2
  • SQLServer

非关系型数据库(NoSql):

下载后得到下面安装包:
* Redis的使用 - 图2

2.2.2 Redis安装

1)在Linux中安装Redis
在Linux系统安装Redis步骤:

  1. 将Redis安装包上传到Linux到soft目录
  2. 解压安装包,命令:tar -zxvf redis-4.0.0.tar.gz -C /usr/local
  3. 安装Redis的依赖环境gcc,命令:yum install gcc-c++
  4. 进入/usr/local/redis-4.0.0,进行编译,命令:make
  5. 进入redis的src目录进行安装,命令:make install
  6. 进入/usr/local/redis-4.0.0 ,把redis.conf文件拷贝到src目录中
  7. 修改redis.conf文件,需要修改的地方有:
    1. 修改redis.conf文件,让其在后台启动不要霸屏的方式启动, 将配置文件中的daemonize配置项改为yes,默认值为no。
    2. reids默认是没有密码的,如果你需要有密码,将配置文件中的 # requirepass foobared 配置项取消注释,默认为注释状态。foobared为密码,可以根据情况自己指定。(选做)
    3. redis的服务默认只是允许本机连接,其他机器默认情况是不被允许连接,如果允许其他机器也能连接linux的reids服务,那么需要修改bind 127.0.0.1 你自己的linux机器的ip地址
  8. 启动redis的服务, 使用 redis-server redis.conf
  9. 启动客户端去连接服务端测试: 启动客户端的方式:
    1. 方式一(没有密码方式): 在src目录中 ./redis-cli

* Redis的使用 - 图3
2. 方式二(如果存在密码情况): 在src目录中: ./redis-cli -h 127.0.0.1 -p 端口号 -a 密码
* Redis的使用 - 图4
2)在Windows中安装Redis
Redis的Windows版属于绿色软件,直接解压即可使用,解压后目录结构如下:
* Redis的使用 - 图5

2.3 Redis服务启动与停止

1)Linux系统中启动和停止Redis

执行Redis服务启动脚本文件redis-server:
* Redis的使用 - 图6
通过启动日志可以看到,Redis默认端口号为6379。
Ctrl + C停止Redis服务
通过redis-cli可以连接到本地的Redis服务,默认情况下不需要认证即可连接成功。
退出客户端可以输入exit或者quit命令。

2)** Windows系统中启动和停止Redis

在控制台执行redis-server redis.

要加载conf的配置文件: >redis-server.exe redis.windows.conf image.png

* Redis的使用 - 图8
Ctrl + C停止Redis服务
双击redis-cli.exe即可启动Redis客户端,默认连接的是本地的Redis服务,而且不需要认证即可连接成功。
* Redis的使用 - 图9
退出客户端可以输入exit或者quit命令。

2.5 redis客户端程序

在今天的资料里面有redis的图形化界面工具,只需要双击不断下一步即可
* Redis的使用 - 图10
连接redis服务器
* Redis的使用 - 图11
* Redis的使用 - 图12

3. Redis数据类型

3.1 介绍

Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:

  • 字符串 string
  • 哈希 hash
  • 列表 list
  • 集合 set
  • 有序集合 sorted set / zset

    3.2 Redis 5种常用数据类型

* Redis的使用 - 图13
解释说明:

字符串(string):普通字符串,常用 哈希(hash):适合存储对象 列表(list):按照插入顺序排序,可以有重复元素 集合(set):无序集合,没有重复元素 有序集合(sorted set / zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素

4. Redis常用命令

4.1 * 字符串string操作命令:

Redis 中字符串类型常用命令:

  • SET key value 设置指定key的值
  • GET key 获取指定key的值
  • SETEX key seconds value 设置指定key的值,并将 key 的过期时间设为 seconds 秒
  • SETNX key value 只有在 key 不存在时设置 key 的值
    • 拓展:可以通过该命令设置分布式锁;
  • INCR key:让key的值自增
    • 作用:可以作为分布式id,往数据库中新增记录的时候,可以让他维护主键;
    • 应用场景:
      • 在MySQL中一张表只能存放一千万条数据,但是要将全国人口信息都放到表中的话,就要将表进行拆分;
      • 在插入记录每张表的id如果使用主键自增的方式的话默认都是从1开始的,就会出现id相同的人。这个时候就不能使用id自增了,可以使用其他的方式维护主键值,RedisINCR就是其中的方式之一:
        • 要往mysql中插入一条数据的时候,先将获取在Redis下自增的key值,获取好后再将该值存入mysql对应的主键id即可;
        • 总之,做到主键id不能重复即可,类似的方式还有雪花算法,唯一数id算法等等;

* Redis的使用 - 图14
更多命令可以参考Redis中文网:https://www.redis.net.cn

4.2 哈希hash操作命令

Redis hash 是一个string类型的 field 和 value 的映射表,hash特别适合用于存储对象,常用命令:

  • HSET key field value 将哈希表 key 中的字段 field 的值设为 value
  • HGET key field 获取存储在哈希表中指定字段的值
  • HDEL key field 删除存储在哈希表中的指定字段
  • HKEYS key 获取哈希表中所有字段
  • HVALS key 获取哈希表中所有值
  • HGETALL key 获取在哈希表中指定 key 的所有字段和值

* Redis的使用 - 图15
* Redis的使用 - 图16

4.3 列表list操作命令

Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:

  • LPUSH key value1 [value2] 将一个或多个值插入到列表头部
  • LRANGE key start stop 获取列表指定范围内的元素
  • RPOP key 移除并获取列表最后一个元素
  • LLEN key 获取列表长度
  • BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超 时或发现可弹出元素为止

* Redis的使用 - 图17
* Redis的使用 - 图18

4.4 * 集合set操作命令

Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:

  • SADD key member1 [member2] 向集合添加一个或多个成员
  • SMEMBERS key 返回集合中的所有成员
  • SCARD key 获取集合的成员数
  • SINTER key1 [key2] 返回给定所有集合的交集
  • SUNION key1 [key2] 返回所有给定集合的并集
  • SDIFF key1 [key2] 返回给定所有集合的差集
  • SREM key member1 [member2] 移除集合中一个或多个成员

* Redis的使用 - 图19
* Redis的使用 - 图20

4.5 * 有序集合sorted set操作命令

Redis sorted set 有序集合是 string 类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score) 。redis正是通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。
常用命令:

  • ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的 分数
  • ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合中指定区间内的成员
  • ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment
  • ZREM key member [member …] 移除有序集合中的一个或多个成员

* Redis的使用 - 图21

应用场景:热点排行榜什么的;

4.6 通用命令

Redis中的通用命令,主要是针对key进行操作的相关命令:

  • KEYS pattern 查找所有符合给定模式( pattern)的 key
  • EXISTS key 检查给定 key 是否存在
  • TYPE key 返回 key 所储存的值的类型
  • TTL key 返回给定 key 的剩余生存时间(TTL, time to live),以秒为单位
  • DEL key 该命令用于在 key 存在是删除 key

    5. ** 在Java中操作Redis

    5.1 介绍

    前面我们讲解了Redis的常用命令,这些命令是我们操作Redis的基础,那么我们在java程序中应该如何操作Redis呢?这就需要使用Redis的Java客户端,就如同我们使用JDBC操作MySQL数据库一样。
    Redis 的 Java 客户端很多,官方推荐的有三种:

  • Jedis

  • Lettuce
  • Redisson

Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis。

5.2 Jedis

Jedis 是 Redis 的 Java 版本的客户端实现。
maven坐标:

  1. <dependencies>
  2. <dependency>
  3. <groupId>redis.clients</groupId>
  4. <artifactId>jedis</artifactId>
  5. <version>2.8.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>junit</groupId>
  9. <artifactId>junit</artifactId>
  10. <version>4.12</version>
  11. </dependency>
  12. </dependencies>

* 使用 Jedis 操作 Redis 的步骤:

  1. 获取连接
  2. 执行操作
  3. 关闭连接

示例代码:

package com.itheima.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class JedisTest {
    @Test
    public void test01(){
        //1. 创建jedis并且连接到redis的服务端
        Jedis jedis = new Jedis("192.168.206.128",6379);
        //2. 存储数据
        jedis.set("name","狗娃");
        //3. 获取数据
        System.out.println("获取到的数据:"+jedis.get("name"));
        //4. 关闭连接
        jedis.close();
    }
}

5.3 Spring Data Redis

5.3.1 介绍

Spring Data Redis 是 Spring 的一部分,提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。
网址:https://spring.io/projects/spring-data-redis
* Redis的使用 - 图22
maven坐标:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.4.8</version>
</dependency>

Spring Boot提供了对应的Starter,maven坐标:

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

Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对 Jedis 客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:

  • ValueOperations:简单K-V操作
  • SetOperations:set类型数据操作作
  • ZSetOperations:zset类型数据操作
  • HashOperations:针对hash类型的数据操作
  • ListOperations:针对list类型的数据操作

    5.3.2 使用方式

    5.3.2.1 ** 环境搭建
    第一步:创建maven项目springdataredis_demo,配置pom.xml文件 ```xml org.springframework.boot spring-boot-starter-parent 2.4.5
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

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

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.4.5</version>
        </plugin>
    </plugins>
</build>
第二步:编写启动类
```java
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

第三步:配置application.yml

spring:
  redis:
    host: 192.168.206.128
    port: 6379
    #    password:  有密码你才写
    database: 0 #redis默认有16个数据库, 操作的数据库是哪个
    jedis:
      pool:
        max-active: 10 #最大链接数,连接池中最多有10个
        max-idle: 5   # 最大空闲数
        min-idle: 1   #最小空闲数
        #举个例子: 连接池初始化3个链接, 客户拿走2个链接,空闲1个,达到最小的空闲数,必须马上增加
        max-wait: 1ms #连接池最大阻塞等待时间

解释说明:

spring.redis.database:指定使用Redis的哪个数据库,Redis服务启动后默认有16个数据库,编号分别是从0到15。 可以通过修改Redis配置文件来指定数据库的数量。

第四步:(作用 :设置 java对象与redis 键与值的序列化)
springdata_redis默认帮我们创建的RedisTemplate对象是使用jdk的序列号器帮我们键与值存储到redis中,而jdk的序列号器对键与值是采用二进制的方式存储的,所以我们会看到乱码的情况。如果我们需要看得懂,那么需要修改redistemplate使用的序列号器才行。 提供配置类:RedisTemplate

package com.itheima.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;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class MyRedisAutoConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //对象存进去的会自动转成json字符串,取出来同样的也会将json字符串转为java对象;
        template.setKeySerializer(new StringRedisSerializer()); //key的序列号器
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //值序列号器
        return template;
    }


    //示例2:
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = getJsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jsonRedisSerializer);
        // 支持事物
        //template.setEnableTransactionSupport(true);
        template.afterPropertiesSet();
        return template;
    }

}

总结,存字符串就用 StringRedisTemplate 注入;字符串以外就最好用 RedisTemplate(这个类是泛型)注入而,RedisTemplate 是常用(掌握);且 RedisTemplate 需要自己进行配置;

  • 如果在各种可视化客户端中取出的数据是编程字节数组,那就配置序列化器 Template(见上) ;(效果如下)
//注入RedisTemplate;(泛型)
@Autowired
    RedisTemplate redisTemplate;

//还有一个是: StringRedisTemplate
//@Autowired
//    StringRedisTemplate stringRedisTemplate;

@Test
    void setTextRedisTemplate () {

        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("hello" , "it is redis");
        Object hello = valueOperations.get("hello");

        System.out.println("hello = " + hello);

        HashMap <String, Object> hashMap = new HashMap <>();
        hashMap.put("A","b");
        hashMap.put("A",2);

        valueOperations.set("A",hashMap);
        Object a = valueOperations.get("A");
        //a = {A=2}
        System.out.println("a = " + a);

    }
  • 设置前:image.pngimage.png
  • 设置后:image.pngimage.png
  • 拓展:其实他们两者之间的 区别主要在于他们使用的序列化类
    • RedisTemplate 使用的是 JdkSerializationRedisSerializer 存入数据会将数据先序列化成字节数组然后在存入Redis数据库。(默认使用了jdk的序列化方式)
    • StringRedisTemplate 使用的是 StringRedisSerializer
  • RedisTemplate 使用时常见问题:
    • redisTemplate 中存取数据都是字节数组。当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate 取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。

添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>springdataredis_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
      *****
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.11.1</version>
        </dependency>
      *****
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.5</version>
            </plugin>
        </plugins>
    </build>
</project>

解释说明:

当前配置类不是必须的,因为 Spring Boot 框架会自动装配 RedisTemplate 对象,但是默认的key序列化器为JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别

第五步:提供测试类

package com.itheima.test;
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.*;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class RedisTemplateTest {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

}

5.3.2.2 ** 操作字符串类型数据
package com.itheima.test;
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.*;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class RedisTemplateTest {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    /**
     * 操作字符串类型
     */
    @Test
    public void testStr(){
        //1. 得到操作字符串类型的对象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //2. 操作字符串
        //存储
        valueOperations.set("name","狗娃");
        //获取
        System.out.println("获取到的值:"+ valueOperations.get("name"));
        //设置过期时间
        valueOperations.set("age","18",1, TimeUnit.MINUTES); //设置过期时间为一分钟
        //如果不存在key则添加,则则不添加
        valueOperations.setIfAbsent("name","狗剩");
    }
    /**
     * 操作hash类型
     */
    @Test
    public void testHash(){
        //1. 得到操作hash类型的客户端对象
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        //存储
        hashOperations.put("person","id","110");
        hashOperations.put("person","name","狗娃");
        //取
        System.out.println("姓名:"+ hashOperations.get("person","name"));
        //所有field
        Set<Object> set = hashOperations.keys("person");
        for (Object key : set) {
            System.out.print(key+",");
        }
        System.out.println();
        //所有值
        List<Object> list = hashOperations.values("person");
        for (Object value : list) {
            System.out.print(value+",");
        }
        System.out.println();
    }

}

5.3.2.3 **操作哈希类型数据
/**
     * 操作hash类型
     */
    @Test
    public void testHash(){
        //1. 得到操作hash类型的客户端对象
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        //存储
        hashOperations.put("person","id","110");
        hashOperations.put("person","name","狗娃");
        //取
        System.out.println("姓名:"+ hashOperations.get("person","name"));
        //所有field
        Set<Object> set = hashOperations.keys("person");
        for (Object key : set) {
            System.out.print(key+",");
        }
        System.out.println();
        //所有值
        List<Object> list = hashOperations.values("person");
        for (Object value : list) {
            System.out.print(value+",");
        }
        System.out.println();
    }

5.3.2.4 ** 操作列表类型数据
/**
     * 操作list类型
     */
    @Test
    public void testList(){
        //1. 得到操作list对象的客户端对象
        ListOperations<String, String> listOperations = redisTemplate.opsForList();
        //2. 存
        listOperations.leftPushAll("list","张三","李四","王五");
        //3. 取
        System.out.println("list集合的元素:"+listOperations.range("list",0,-1));
        //4.得到长度
        System.out.println("长度:"+ listOperations.size("list"));
    }

5.3.2.5 ** 操作集合类型数据
/**
     * 操作set类型
     */
    @Test
    public void testSet(){
        //1. 得到操作set对象的客户端对象
        SetOperations<String, String> setOperations = redisTemplate.opsForSet();
        //存
        setOperations.add("set","洋洋","张庭","凡凡","签签","洋洋");
        //取
        Set<String> set = setOperations.members("set");
        set.forEach(System.out::println);
        //删
        setOperations.remove("set","洋洋");
        //查看内容
        System.out.println("=========删除后的内容=========");
        set = setOperations.members("set");
        set.forEach(System.out::println);
    }

5.3.2.6 ** 操作有序集合类型数据
/**
     * 操作zset类型
     */
    @Test
    public void testzSet(){
        //1. 得到操作zset对象的客户端对象
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        //2. 存
        zSetOperations.add("zset1","洋洋",100);
        zSetOperations.add("zset1","林荣",180);
        zSetOperations.add("zset1","尚晋",250);
        //取
        Set<String> zset1 = zSetOperations.range("zset1", 0, -1);
        for (String name : zset1) {
            System.out.println("姓名:"+ name+" 分数:"+zSetOperations.score("zset1",name));
        }
        //修改
        zSetOperations.incrementScore("zset1","尚晋",-2);
        //取
        System.out.println("=======修改后的分数==========");
        zset1 = zSetOperations.range("zset1", 0, -1);
        for (String name : zset1) {
            System.out.println("姓名:"+ name+" 分数:"+zSetOperations.score("zset1",name));
        }
    }

5.3.2.7 通用操作
/**
     * 通用操作
     */
    @Test
    public void baseTest(){
        //1. 所有键
        Set<String> keys = redisTemplate.keys("*");
        System.out.println("=======所有键==========");
        for (String key : keys) {
            System.out.print(key+",");
        }
        System.out.println();
        //2. 判断有没有键
        Boolean flag = redisTemplate.hasKey("set");
        System.out.println("是否存在set1:"+ flag);
        //3. 类型
        System.out.println("类型:"+redisTemplate.type("set"));
        //4. 删除
        redisTemplate.delete("set");
    }

6. SpringBoot:启动器&自动配置(拓展)

使用SpringBoot之后,整合SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这是如何做到的?

目标:Spring Boot的核心:启动器&自动配置原理

官方启动器介绍: https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/using-boot-build-systems.html#using-boot-starter

10.1 启动器介绍

  • Spring Boot提供的启动器
    * Redis的使用 - 图27
    * Redis的使用 - 图28
    * Redis的使用 - 图29
  • Spring Boot启动器的作用

    • 配置一个启动器它会把整合这个框架或模块的依赖全部导入。
    • 每一个启动器都有一个自动配置类,实现自动整合Spring。
    • 每一个启动器都有一个属性类,提供了默认的属性配置。

      10.2 自动配置原理

  • 一切魔力的开始,来自启动类的main方法:
    * Redis的使用 - 图30

    特别的两个地方:

    1. 注解: @SpringBootApplication 【重点】
    2. run方法: SpringApplication.run() 运行spring应用(创建spring容器)
  • @SpringBootApplication 相当于三个注解的组合

    • @SpringBootConfiguration 【作用: 定义配置类】
    • @EnableAutoConfiguration 【作用: 启用自动配置】
    • @ComponentScan 【作用: 扫描Spring容器】

      注解@EnableAutoConfiguration,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了spring-boot-starter-web,而这个启动器中帮我们添加了tomcatSpringMVC的依赖。此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了!

    • @ComponentScan 【作用: 组件扫描】

      配置组件扫描的指令。提供了类似与<context:component-scan>标签的作用 通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包

    • * Redis的使用 - 图31

  • 自动配置实现流程
    • SpringApplication在运行Spring应用时,是通过SpringFactoriesLoader的loadSpringFactories()方法,初始化Spring工厂实例。
    • 实例化SpringApplication时做了什么?
      • SpringApplication的构造方法,其中做了几件事情
      • 推断WebApplicationType,主要思想就是在当前的classpath下搜索特定的类
      • 搜索META-INF\spring.factories文件配置的ApplicationContextInitializer的实现类
      • 搜索META-INF\spring.factories文件配置的ApplicationListenerr的实现类
      • 推断MainApplication的Class
    • SpringApplication的run方法做了什么?

(下看一下run方法的代码,代码不多,做的事情很多。)
SpringApplication的run方法

  1. 创建一个StopWatch并执行start方法,这个类主要记录任务的执行时间
  1. 配置Headless属性,Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置
  1. 在文件META-INF\spring.factories中获取SpringApplicationRunListener接口的实现类EventPublishingRunListener,主要发布SpringApplicationEvent
  1. 把输入参数转成DefaultApplicationArguments类
  1. 创建Environment并设置比如环境信息,系统熟悉,输入参数和profile信息
  1. 打印Banner信息
  1. 创建Application的上下文,根据WebApplicationTyp来创建Context类,如果非web项目则创建AnnotationConfigApplicationContext,在构造方法中初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
  1. 在文件META-INF\spring.factories中获取SpringBootExceptionReporter接口的实现类FailureAnalyzers
  1. 准备application的上下文
     1. 初始化ApplicationContextInitializer
     1. 执行Initializer的contextPrepared方法,发布ApplicationContextInitializedEvent事件
     1. 如果延迟加载,在上下文添加处理器LazyInitializationBeanFactoryPostProcessor
     1. 执行加载方法,BeanDefinitionLoader.load方法,主要初始化了AnnotatedGenericBeanDefinition
     1. 执行Initializer的contextLoaded方法,发布ApplicationContextInitializedEvent事件
     1. 刷新上下文(**后文会单独分析refresh方法**),在这里真正加载bean到容器中。如果是web容器,会在onRefresh方法中创建一个Server并启动。

  • * Redis的使用 - 图32
  • META-INF/spring.factories工厂配置文件
    * Redis的使用 - 图33
    这个key所对应的值,就是所有的自动配置类
    * Redis的使用 - 图34
    可以在当前的jar包中找到这些自动配置类:
    * Redis的使用 - 图35
    每个包都有一个XxxAutoConfiguration配置类,都是一个基于纯注解的配置类,是各种框架整合的代码。
    * Redis的使用 - 图36
    该自动配置类中指定了一些默认属性,在该包下有一个XxxProperties属性类:
    * Redis的使用 - 图37

    10.5 总结

    Spring Boot自动配置原理:
  • SpringApplication会寻找 META-INF/spring.factories 文件,读取其中以EnableAutoConfiguration 为key的所有类的名称, 这些类就是提前写好的自动配置类。
  • 这些配置类不一定全部生效,因为有@ConditionalOn注解,满足一定条件才会生效, 通过自动配置类的@bean注解所在的方法创建对应的对象
  • 我们可以通过配置application.yml或application.properties文件,来覆盖自动配置中的默认属性。

    7. SpringBoot:自动装配应用-案例练习(拓展)

    案例描述

    需求:自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。

    步骤

  1. 创建工程导入依赖
  2. 编写jedis自动配置类
  3. 编写jedis属性配置类
  4. 编写application.yml 编写redis的配置属性
  5. 测试

    实现

    1、创建工程导入依赖
    <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.4.5</version>
     </parent>
     <dependencies>
         <!--导入spring的容器-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>
         </dependency>
         <!--导入jedis-->
         <dependency>
             <groupId>redis.clients</groupId>
             <artifactId>jedis</artifactId>
             <version>2.9.3</version>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
     </dependencies>
    
    2、编写jedis自动配置类
    package cn.itcast.config;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import redis.clients.jedis.Jedis;
    @EnableConfigurationProperties(RedisProperties.class)
    @Configuration
    public class RedisConfiguration {
     @Autowired(required = false)
    private RedisProperties redisProperties;
     @Bean
     public Jedis getJedis(){
         return new Jedis(redisProperties.getHost(),redisProperties.getPort());
     }
    }
    
    4、编写jedis属性配置类
    package cn.itcast.config;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    @ConfigurationProperties("my")
    @Data
    public class RedisProperties {
     private Integer port;
     private String host;
    }
    
    5、编写application.yml 编写redis的配置属性
    my:
    port: 6379
    host: 127.0.0.1
    
    6、测试
    package cn.itcast.test;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import redis.clients.jedis.Jedis;
    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class AppTest {
     @Autowired(required = false)
     private Jedis jedis;
     @Test
     public void test01(){
         jedis.set("name", "张三");
         String name = jedis.get("name");
         System.out.println("redis中获取到的数据:"+ name);
     }
    }
    

    小结

  • 自定义启动器的步骤?