Redis基础
课程内容
- Redis入门
- Redis数据类型
- Redis常用命令
-
1. 前言
1.1 什么是Redis
redis是一款非关系型数据库,redis存储的数据是在内存中的。特点:可以快速读取到数据,因为存储的数据是在内存中的。Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。
基于内存存储,读写性能高(运行内存)
- 适合存储热点数据(热点商品、资讯、新闻),一般数据一般变化频率比较低,或者是高并发请求数据
- 企业应用广泛
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
- Mongo db
-
2.2 Redis下载与安装
2.2.1 Redis下载
Redis安装包分为windows版和Linux版:
Windows版下载地址:https://github.com/microsoftarchive/redis/releases
- Linux版下载地址: https://download.redis.io/releases/
2.2.2 Redis安装
1)在Linux中安装Redis
在Linux系统安装Redis步骤:
- 将Redis安装包上传到Linux到soft目录
- 解压安装包,命令:tar -zxvf redis-4.0.0.tar.gz -C /usr/local
- 安装Redis的依赖环境gcc,命令:yum install gcc-c++
- 进入/usr/local/redis-4.0.0,进行编译,命令:make
- 进入redis的src目录进行安装,命令:make install
- 进入/usr/local/redis-4.0.0 ,把redis.conf文件拷贝到src目录中
- 修改redis.conf文件,需要修改的地方有:
- 修改redis.conf文件,让其在后台启动不要霸屏的方式启动, 将配置文件中的daemonize配置项改为yes,默认值为no。
- reids默认是没有密码的,如果你需要有密码,将配置文件中的 # requirepass foobared 配置项取消注释,默认为注释状态。foobared为密码,可以根据情况自己指定。(选做)
- redis的服务默认只是允许本机连接,其他机器默认情况是不被允许连接,如果允许其他机器也能连接linux的reids服务,那么需要修改bind 127.0.0.1 你自己的linux机器的ip地址
- 启动redis的服务, 使用 redis-server redis.conf
- 启动客户端去连接服务端测试: 启动客户端的方式:
- 方式一(没有密码方式): 在src目录中 ./redis-cli
2. 方式二(如果存在密码情况): 在src目录中: ./redis-cli -h 127.0.0.1 -p 端口号 -a 密码
2)在Windows中安装Redis
Redis的Windows版属于绿色软件,直接解压即可使用,解压后目录结构如下:
2.3 Redis服务启动与停止
1)Linux系统中启动和停止Redis
执行Redis服务启动脚本文件redis-server:
通过启动日志可以看到,Redis默认端口号为6379。
Ctrl + C停止Redis服务
通过redis-cli可以连接到本地的Redis服务,默认情况下不需要认证即可连接成功。
退出客户端可以输入exit或者quit命令。
2)** Windows系统中启动和停止Redis
在控制台执行redis-server redis.
要加载conf的配置文件: >redis-server.exe redis.windows.conf
Ctrl + C停止Redis服务
双击redis-cli.exe即可启动Redis客户端,默认连接的是本地的Redis服务,而且不需要认证即可连接成功。
退出客户端可以输入exit或者quit命令。
2.5 redis客户端程序
在今天的资料里面有redis的图形化界面工具,只需要双击不断下一步即可
连接redis服务器
3. Redis数据类型
3.1 介绍
Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:
解释说明:
字符串(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自增了,可以使用其他的方式维护主键值,Redis的INCR就是其中的方式之一:
- 要往mysql中插入一条数据的时候,先将获取在Redis下自增的key值,获取好后再将该值存入mysql对应的主键id即可;
- 总之,做到主键id不能重复即可,类似的方式还有雪花算法,唯一数id算法等等;
更多命令可以参考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 的所有字段和值
4.3 列表list操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
- LPUSH key value1 [value2] 将一个或多个值插入到列表头部
- LRANGE key start stop 获取列表指定范围内的元素
- RPOP key 移除并获取列表最后一个元素
- LLEN key 获取列表长度
- BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超 时或发现可弹出元素为止
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] 移除集合中一个或多个成员
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 …] 移除有序集合中的一个或多个成员
应用场景:热点排行榜什么的;
4.6 通用命令
Redis中的通用命令,主要是针对key进行操作的相关命令:
- KEYS pattern 查找所有符合给定模式( pattern)的 key
- EXISTS key 检查给定 key 是否存在
- TYPE key 返回 key 所储存的值的类型
- TTL key 返回给定 key 的剩余生存时间(TTL, time to live),以秒为单位
-
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坐标:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
* 使用 Jedis 操作 Redis 的步骤:
- 获取连接
- 执行操作
- 关闭连接
示例代码:
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
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文件 ```xmlorg.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);
}
- 设置前:
- 设置后:
- 拓展:其实他们两者之间的 区别主要在于他们使用的序列化类:
- 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提供的启动器
Spring Boot启动器的作用
一切魔力的开始,来自启动类的main方法:
特别的两个地方:
- 注解: @SpringBootApplication 【重点】
- run方法: SpringApplication.run() 运行spring应用(创建spring容器)
@SpringBootApplication 相当于三个注解的组合
- @SpringBootConfiguration 【作用: 定义配置类】
- @EnableAutoConfiguration 【作用: 启用自动配置】
@ComponentScan 【作用: 扫描Spring容器】
注解
@EnableAutoConfiguration
,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了spring-boot-starter-web
,而这个启动器中帮我们添加了tomcat
、SpringMVC
的依赖。此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了!@ComponentScan 【作用: 组件扫描】
配置组件扫描的指令。提供了类似与
<context:component-scan>
标签的作用 通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包
- 自动配置实现流程
- 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并启动。
- META-INF/spring.factories工厂配置文件
这个key所对应的值,就是所有的自动配置类
可以在当前的jar包中找到这些自动配置类:
每个包都有一个XxxAutoConfiguration配置类,都是一个基于纯注解的配置类,是各种框架整合的代码。
该自动配置类中指定了一些默认属性,在该包下有一个XxxProperties属性类:
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。步骤
- 创建工程导入依赖
- 编写jedis自动配置类
- 编写jedis属性配置类
- 编写application.yml 编写redis的配置属性
- 测试
实现
1、创建工程导入依赖
2、编写jedis自动配置类<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>
4、编写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()); } }
5、编写application.yml 编写redis的配置属性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; }
6、测试my: port: 6379 host: 127.0.0.1
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); } }
小结
- 自定义启动器的步骤?