开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

docker 同主机/host 容器间网络互联互通

发表于 2021-08-07

“这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战”

对于复杂的应用,不可避免需要多个服务部署在多个容器中,并且服务间存在网络互联通信的情况。比如服务 A 需要连接另一个 mysql 的容器。

1. 新建网络

先创建一个新的 Docker 网络

1
bash复制代码docker network create -d bridge --subnet 172.27.0.0/16 cloud-net
  • -d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode。
  • subnet 参数表示新建了 172.27.0.0/255.255.0.0 的网络,名称为 cloud-net。

新建网络是为了实现同主机的多个容器间网络互通,走内网,类似于软件交换机。

2. 查看网络

可以通过 network ls 命令查看当前宿主机中所有的 docker 网络

1
2
3
4
5
bash复制代码[root@192 ~] docker network ls
NETWORK ID NAME DRIVER SCOPE
02de4daaa2c4 subnet bridge local
4764e0697c35 host host local
60da7fd0b38e none null local

可以看到目前系统中已有的内部网络列表

建议将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 –link 参数

3. 连接容器

运行一个容器并连接到新建的 cloud-net 网络

1
bash复制代码docker run -it --rm --network=cloud-net --ip=172.27.0.20 --name cloud1 cloud-image
  • --network=cloud-net --ip=172.27.0.20 是为了固定当前容器的 IP 地址
  • cloud-image 表示当前使用的镜像(替换为实际镜像名称)

打开新的终端,再运行一个容器并加入到 cloud-net 网络

1
bash复制代码docker run -it --rm --network=cloud-net --ip=172.27.0.30 --name cloud2 cloud-image

在 cloud1 容器输入以下命令来证明 cloud1 容器和 cloud2 容器建立了互联关系

1
2
bash复制代码ping cloud2 
#or ping 172.27.0.30

这样在 cloud1 中就可以通过 ip 172.27.0.30 访问 cloud2 容器了。同台服务器安装N个容器中,就可以通过 docker 自建的网络实现互通

容器间不能使用 127.0.0.1 连接

4. 容器网络示意图

可以通过docker 容器网络示意图,清晰明白docker 的网络:

微信截图_20210807083055.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Linux的I/O模型 —— select/poll/epo

发表于 2021-08-07

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

select/poll/epoll对比:

select、poll、epoll对比.png

注意:遍历相当于查看所有的位置,回调相当于查看对应的位置。

1 select

select工作流程.jpg

POSIX所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理

缺点

  • 单个进程可监视的fd数量被限制,即能监听端口的数量有限,数值存在如下文件里:cat /proc/sys/fs/file-max
  • 对socket是线性扫描,即采用轮询的方法,效率较低
  • select采取了内存拷贝方法来实现内核将FD消息通知给用户空间,这样一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

select是第一版IO复用,提出后暴漏了很多问题。

  • select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的
  • select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但不会告诉是那个sock上有数据,只能自己遍历查找
  • select 只能监视1024个链接
  • select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现这个sock不用,要收回,这个select 不支持的

2 poll

poll工作流程.jpg

本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态

  • 其没有最大连接数的限制,原因是它是基于链表来存储的
  • 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义
  • poll特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd
  • 边缘触发:只通知一次,epoll用的就是边缘触发

poll 修复了 select 的很多问题:

  • poll 去掉了1024个链接的限制
  • poll 从设计上来说不再修改传入数组

但是poll仍然不是线程安全的, 这就意味着不管服务器有多强悍,你也只能在一个线程里面处理一组 I/O 流。你当然可以拿多进程来配合了,不过然后你就有了多进程的各种问题。

3 epoll

epoll工作流程.jpg

在Linux2.6内核中提出的select和poll的增强版本

  • 支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次
  • 使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知

优点

  • 没有最大并发连接的限制:能打开的FD的上限远大于1024(1G的内存能监听约10万个端口)
  • 效率提升:非轮询的方式,不会随着FD数目的增加而效率下降;只有活跃可用的FD才会调用callback函数,即epoll最大的优点就在于它只管理“活跃”的连接,而跟连接总数无关
  • 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销
  • 文件映射内存直接通过地址空间访问,效率更高,把文件映射到内存中

epoll 可以说是 I/O 多路复用最新的一个实现,epoll 修复了poll 和select绝大部分问题, 比如:

  • epoll 现在是线程安全的
  • epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了
  • epoll 内核态管理了各种IO文件描述符, 以前用户态发送所有文件描述符到内核态,然后内核态负责筛选返回可用数组,现在epoll模式下所有文件描述符在内核态有存,查询时不用传文件描述符进去了

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SpringBoot整合Redis实现消息队列

发表于 2021-08-06

写这篇文章的原因还是得归咎于👇

上一篇博客写了👉 SpringBoot整合Redis实现发布/订阅模式

以及上上一篇博客写了👉Docker搭建Redis Cluster 集群环境

我自己是认为对于每个知识点,光看了不操作是没有用的(遗忘太快…),多少得在手上用上几回才可以,才能对它加深印象。

昨天搭建了Redis Cluster 集群环境,今天就来拿它玩一玩Redis 消息队列吧

于是便有了这个Redis 实现消息队列的Demo,

很喜欢一句话:”八小时内谋生活,八小时外谋发展“。

共勉.😁

地点:家里看到的云

作者:😁

一、前言

概念

消息队列:“消息队列”是在消息的传输过程中保存消息的容器。

其实就是个 生产者--->消息队列<---消费者 的模型。集群就是蛮多蛮多而已。

作用:

主要解决应用耦合,异步消息,流量削锋等问题

应用场景:

异步处理,应用解耦(拆分多系统),流量削峰(秒杀活动、请求量过大)和消息通讯(发布公告、日志)四个场景。

此处只演示了最简单的一个图哈。

举例子:异步消息

在这里插入图片描述

使用消息队列后

在这里插入图片描述

消息中间件其实市面上已经有很多,如RabbitMq,RocketMq、ActiveMq、Kafka等,我拿Redis来做消息队列,其本意是1)为了熟悉Redis;2)Redis 确实可以来做简单的消息队列(狗头保命)

二、前期准备

就是需要个Redis,其他的倒是没啥特殊的啦。😁

2.1、项目结构

一普通的SpringBoot的项目…😊

在这里插入图片描述

2.2、依赖的jar包

jar 也都是一些正常的jar包哈,没啥新奇玩意。😜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
xml复制代码<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2.3、yml配置文件

分单机和集群,主要是上一篇文章带的….🙄😶

单机配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
yml复制代码spring:
redis:
database: 0
port: 6379
host: localhost
password:
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 1024
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 10000
# 连接池中的最大空闲连接
max-idle: 200
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 10000

redis集群配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
yml复制代码server:
port: 8089
spring:
application:
name: springboot-redis
redis:
password: 1234
cluster:
nodes:
- IP地址:6379
- IP地址:6380
- IP地址:6381
- IP地址:6382
- IP地址:6383
- IP地址:6384
max-redirects: 3 # 获取失败 最大重定向次数
lettuce:
pool:
max-active: 1000 #连接池最大连接数(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接

#===========jedis配置方式=============================================
# jedis:
# pool:
# max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
# max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
# max-idle: 10 # 连接池中的最大空闲连接
# min-idle: 5 # 连接池中的最小空闲连接
#

三、编码

3.1、config层

没有什么特殊的配置,🤗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
java复制代码import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* redis 配置类
* 1. 设置RedisTemplate序列化/返序列化
*
* @author cuberxp
* @since 1.0.0
* Create time 2020/1/23 0:06
*/
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//设置value hashValue值的序列化
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashValueSerializer(serializer);
//key hasKey的序列化
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

3.2、信息实体类

加个实体类,模拟传递信息中需要用到的实体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* @author crush
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AnnouncementMessage implements Serializable {

private static final long serialVersionUID = 8632296967087444509L;

private String id;

/*** 内容 */
private String content;
}

3.3、MyThread类

随项目启动而启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
* @Author: crush
* @Date: 2021-08-06 22:17
* version 1.0
* ApplicationRunner:
* 用于指示 bean 在包含在SpringApplication时应该运行的SpringApplication 。
* 通俗说就是 在这个项目运行的时候,它也会自动运行起来。
*/
@Component
public class MyThread implements ApplicationRunner {

@Autowired
MessageConsumerService messageConsumerService;

@Override
public void run(ApplicationArguments args) throws Exception {
messageConsumerService.start();
}
}

3.4、消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
java复制代码import java.util.concurrent.TimeUnit;
import com.crush.queue.entity.AnnouncementMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


/**
* ApplicationRunner 实现这个接口可以跟随项目启动而启动
* @author crush
*/
@Service
public class MessageConsumerService extends Thread {


@Autowired
private RedisTemplate<String,Object> redisTemplate;

private volatile boolean flag = true;

private String queueKey="queue";

private Long popTime=1000L;

@Override
public void run() {
try {
AnnouncementMessage message;
// 为了能一直循环而不结束
while(flag && !Thread.currentThread().isInterrupted()) {
message = (AnnouncementMessage) redisTemplate.opsForList().rightPop(queueKey,popTime,TimeUnit.SECONDS);
System.out.println("接收到了" + message);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}

public boolean isFlag() {
return flag;
}

public void setFlag(boolean flag) {
this.flag = flag;
}

}

3.5、生产者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码import com.crush.queue.entity.AnnouncementMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class MessageProducerService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private String queueKey="queue";

public Long sendMeassage(AnnouncementMessage message) {
System.out.println("发送了" + message);
return redisTemplate.opsForList().leftPush(queueKey, message);
}

}

四、测试

就是简单写了一个测试代码。😝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码import com.crush.queue.entity.AnnouncementMessage;
import com.crush.queue.service.MessageConsumerService;
import com.crush.queue.service.MessageProducerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @Author: crush
* @Date: 2021-08-06 17:11
* version 1.0
*/
@SpringBootTest
public class MessageQueueTest {
@Autowired
private MessageProducerService producer;

@Autowired
private MessageConsumerService consumer;

/**
* 这个测时 的先启动主启动累,
* 然后消费者可以一直在监听。
*/
@Test
public void testQueue2() {
producer.sendMeassage(new AnnouncementMessage("1", "aaaa"));
producer.sendMeassage(new AnnouncementMessage("2", "bbbb"));
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

注:这只是一个小demo ,很多细节都没有去考虑,只是一次对Redis做消息队列的初探,大家见谅。

在这里插入图片描述

五、自言自语

一次由搭建Redis Cluster集群开启的博客,终于结束了,算了好像还没,感觉下次可以多写点实用的。😂🤣

不知道大家学习是什么样的,博主自己的感觉就是学了的东西,要通过自己去梳理一遍,或者说是去实践一遍,我觉得这样子,无论是对于理解还是记忆,都会更加深刻。

如若有不足之处,请不啬赐教!!😁

有疑惑之处,也可以留言或私信,定会第一时间回复。👩‍💻

这篇文章就到这里啦,下篇文章再见。👉一篇文章用Redis 实现消息队列(还在写)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

MySQL规范设计,千万级数据与慢SQL的优化教程

发表于 2021-08-06

说明:该笔记来自高性能可扩展MySQL数据库架构设计与优化,自行整理。

一、项目说明:该笔记的背景为电商平台项目,电商项目由于其高并发、多线程、高耗能等特性,在众多的项目类型中涉及的技术面最广,故以此为例作为案例:

在这里插入图片描述

1、主要功能模块:

在这里插入图片描述

2、对于一个项目的立项,从需求分析到技术栈的决定,其中的业务执行都离不开持久层中对数据库的操作,数据库的库、表设计等规范对项目的高效、可用性都具有很大的影响。以下,对数据库的相关规范进行分析及说明:

数据库设计规范包括:

1
2
3
4
5
6
java复制代码数据库命名规范;
数据库基本设计规范;
数据库索引设计规范;
数据库字段设计规范;
数据库SQL开发规范;
数据库操作行为规范。

二、数据库命名规范

1、所有数据库对象名称必须使用小写字母并用下划线分割

1
2
3
java复制代码不同的数据库名      DbName  dbname

不同的表名 Table table tabLe

2、 所有数据库对象名称禁止使用MySQL保留关键字

1
sql复制代码select id , username , `from` ,age from tb_user

注意:如果一定要使用保留字,那么,就在保留字上添加 `` 符号,数据库在编译时会对其进行判定为普通字符,避免错误

3、数据库对象的命名要能做到见名知意,并且组好,不要超过32个字符

1
2
3
sql复制代码例如:  用户数据库 user_db

用户账号表 user_account

4、临时库表必须以tmp为前缀并以日期为后缀

1
sql复制代码备份库,备份表必须以bak为前缀并以日期为后缀

5、所有存储相同数据的列名和列类型必须一致

在这里插入图片描述

三、数据库基本设计规范

1、所有表必须使用Innodb存储引擎

1
2
3
sql复制代码5.6 以后的默认引擎

支持事务,行级锁,更好的恢复性,高并发下性能更好

2、数据库和表的字符集统一使用UTF8

1
2
3
sql复制代码统一字符集可以避免由于字符集转换产生的乱码

MySQL中UTF8字符集汉字点3个字节,ASCII码占用1个字节

3、所有表和字段都需要添加注释

1
sql复制代码使用 comment 从句添加表和列的备注

4、尽量控制单表数据量的大小,建议控制在500万以内

1
2
3
sql复制代码500万并不是MySQL数据库的限制;

可以用历史数据归档,分库分表等手段来控制数据量大小

MySQL最多可以存储多少万数据呢?

1
sql复制代码这种限制取决于存储设置和文件系统。

5、谨慎使用MySQL分区表

1
2
3
4
5
sql复制代码分区表在物理上表现为多个文件,在逻辑上表现为一个表

谨慎选择分区键,跨分区查询效率可能更低

建议采用物理分表的方式管理大数据

6、尽量做到冷热数据分离,减小表的宽度

1
2
3
4
5
sql复制代码减少磁盘IO,保证热数据的内存缓存命中率

更有效的利用缓存,避免读入无用的冷数据

经常一起使用的列放到一个表中

7、禁止在表中建立预留字段

1
2
3
4
5
sql复制代码预留字段的命名很难做到见名识义

预留字段无法确认存储的数据类型,所以无法选择合适的类型

对预留字段类型的修改,会对表进行锁定

8、禁止在数据库中存储图片,文件等二进制数据

9、禁止在线上做数据库压力测试

10、禁止从开发环境,测试环境直连生产环境数据库

四、数据库索引设计规范

1
2
3
java复制代码索引对数据库的查询性能来说是非常重要的

注意: 不要滥用索引

1、限制每张表上的索引数量,建议单张表索引不超过5个

1
2
3
sql复制代码索引并不是越多越好!索引可以提高效率同样可以降低效率

禁止单个表中的每一列都建立单独的索引

2、每个Innodb表必须有一个主键

1
2
3
4
5
sql复制代码不使用更新频繁的列作为主键,不使用多列主键

不使用UUID,MD5,HASH,字符串列作为主键

主键建议选择使用自增ID值

3、常用索引列建议

1
2
3
4
5
sql复制代码SELECT 、UPDATE、DELETE语句的WHERE从句中的列

包含在ORDER BY、GROUP BY、DISTINCT中的字段

多表JOIN的关联列

4、如何选择索引列的顺序

1
2
3
4
5
sql复制代码区分度最高的列放在联合索引的最左侧

尽量把字段长度小的列放在联合索引的最左侧

使用最频繁的列放到联合索引的左侧

5、避免建立冗余索引和重复索引

1
2
3
sql复制代码primary key(id)、index(id)、unique index(id) --重复索引

index(a,b,c)、index(a,b)、index(a) --联合索引有重复的,造成冗余

6、对于频繁的查询优先考虑使用覆盖索引

1
2
3
4
5
6
sql复制代码覆盖索引:查询走到了索引,并且需要返回的数据刚好是索引的组成字段,换句话说,就是select的字段正好是索引字段,就叫覆盖索引。

好处:
避免Innodb表进行索引的二次查找

可以把随机IO变为顺序IO加快查询效率

7、尽量避免使用外键

1
2
3
sql复制代码外键可用于保证数据的参照完整性,但建议在业务端实现

外键会影响父表和子表的写操作从而降低性能

五、数据库字段设计规范

1、优先选择符合存储需要的最小的数据类型

1
2
3
4
5
sql复制代码将字符串转化为数字类型存储

INET_ATON( `255.255.255.255`) = 4294967295

INET_NTOA( 4294967295) = `255.255.255.255`

2、优先选择符合存储 需要的最小的数据类型

1
2
3
4
5
sql复制代码对于非负数据采用无符号整形进行存储

SIGNED INT -214748~2147483647

UNSIGNED INT 0 ~ 4294967295

3、优先选择符合存储需要的最小的数据类型

1
2
3
4
5
sql复制代码VARCHAR(N) 中的N代表的是字符数,而不是字节数

使用UTF8存储汉字Varchar(255) = 765个字节

过大的长度会消耗更多的内存

4、避免使用TEXT、BLOB数据类型

1
2
3
sql复制代码建议把BLOB或是TEXT列分离到单独的扩展表中

TEXT 或 BLOB类型只能使用前缀索引

5、尽可能把所有列定义为 NOT NULL

1
2
3
sql复制代码索引NULL列需要额外的空间来保存,所以要占用更多的空间

进行比较和计算时要对NULL值做特别的处理

6、避免使用ENUM数据类型

1
2
3
4
5
sql复制代码修改ENUM值需要使用ALTER语句

ENUM类型的OREDR BY操作效率低,需要额外操作

禁止使用数值作为ENUM的枚举值

7、避免在字符串存储日期型的数据(不正确的做法)

1
2
3
sql复制代码缺点1: 无法用日期函数进行计算和比较

缺点2: 用字符串存储日期要占用更多的空间

8、使用TIMESTAMP或DATETIME类型存储时间

1
2
3
sql复制代码TIMESTAMP 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07

TIMESTAMP占用4字节和INT相同,但比INT可读性高

六、SQL开发规范

1、建议使用预编译语句进行数据库操作

在这里插入图片描述

使用预编译语句的好处:

1
2
3
sql复制代码只传参数,比传递SQL语句更高效;

相同语句可以一次解析,多次使用,提高处理效率

2、避免数据类型的隐式转换

1
2
3
sql复制代码隐式转换会导致索引失效

select name,phone from customer where id = `111`

3、充分利用表上已经存在的索引

1
2
3
sql复制代码避免使用双%号的查询条件。如 a like `%123%`,会导致索引失效

一个SQL只能利用到符合索引中的一列进行范围查询

4、程序连接不同的数据库使用不同的账号,禁止跨库查询

1
2
3
4
5
sql复制代码为数据库迁移和分库分表流出余地

降低业务耦合度

避免权限过大而产生的安全风险

5、禁止使用SELECT *,必须使用 SELECT <字段列表> 查询

1
2
3
4
5
sql复制代码消耗更多的CPU和IO以及网络带宽资源

无法使用覆盖索引

可减少表结构变更带来的影响

6、避免使用子查询,可以把子查询优化为JOIN操作

1
2
3
4
5
sql复制代码子查询的结果集无法使用索引

子查询会产生临时表操作,如果子查询数据量大则严重影响效率

消耗过多的CPU及IO资源

7、避免使用JOIN关联太多的表

1
2
3
4
5
sql复制代码每JOIN一个表会多占用一部分内存(join_buffer_size)

会产生临时表操作,影响查询效率

MySQL最多允许关联61个表,建议不超过5个

8、减少同数据库的交互次数

1
2
3
4
5
sql复制代码数据库更适合处理批量操作

合并多个相同的操作到一起,可以提高处理效率

alter table t1 add column c1 int,change column c2 int ...

9、禁止使用 order by rand() 进行随机排序

1
2
3
4
5
sql复制代码a.随机排序会把表中所有符合条件的数据装载到内存中进行排序;

b.随机排序会消耗大量的CPU和IO及内存资源

推荐在程序中获取一个随机值,然后从数据库中获取数据的方式

10、WHERE 从句中禁止对列进行函数转换和计算

1
2
3
4
5
sql复制代码对列进行函数转换或计算会导致无法使用索引

where date(createtime)=`20160901`

where createtime >=`20160901` and createtime <`20160902`

11、在明显不会有重复值时使用UNION ALL 而不是UNION

1
2
3
sql复制代码UNION会把所有数据放到临时表中后再进行去重操作

UNION ALL 不会再对结果及进行去重操作

七、数据库操作行为规范

1、超100万行的批量写操作,要分批多次进行操作

1
2
3
4
5
sql复制代码大批量操作可能会造成严重的主从延迟

binlog日志为row格式时会产生大量的日志

避免产生大失误操作

2、对于大表使用 pt-online-schema-change修改表结构

1
2
3
sql复制代码避免达标修改产生的主从延迟

避免在对表字段进行修改时进行锁表

3、禁止为程序使用的账号赋予super权限

1
2
3
sql复制代码因为当达到最大连接限制时,还允许1个有super权限的用户连接

super权限只能留给DBA处理问题的账号使用

4、对于程序连接数据库账号,遵循权限最小原则

1
2
3
sql复制代码程序使用数据库账号只能在一个DB下使用,不准跨库

程序使用的账号原则上不准有drop权限

Hash分区、RANGE分区、LIST分区

八、MySQL分区表操作

1、定义:数据库表分区是数据库基本设计规范之一,分区表在物理上表现为多个文件,在逻辑上表现为一个表;

2、表分区的弊端: 要谨慎选择分区键,错误的操作可能导致跨分区查询效率降低。

建议 采用物理分表的方式管理大数据。

3、确认MySQL服务器是否支持分区表

使用 SHOW PLUGINS;在mysql命令行查看是否具有分区表的功能:
在这里插入图片描述

查询结果中的 “partition | ACTIVE | STORAGE ENGINE | NULL | GPL “这一行代表当前数据库可以进行数据库分区表操作。

4、普通数据库表的物理结构与分区表的物理结构的区别:

在这里插入图片描述

1
2
3
4
5
6
7
java复制代码分区的sql语句里面多了一句
partition by hash(customer_id) partitions 4;
partition by:根据什么分区
hash:按HASH分区
customer_id:分区键
partitions 4:4个区
图中,左边为普通表的物理结构,右边为分区后的数据库表物理结构。

九、Hash分区表 (按HASH分区)

1、HASH分区的特点

根据MOD(分区键,分区数)的值把数据行存储到表的不同分区中,使数据可以平均的分布在各个分区中。

注意: HASH分区的键值必须是一个INT类型的值,或是通过函数可以转为INT类型。

2、创建HASH分区:

1
2
3
4
5
6
7
sql复制代码use hash;
CREATE TABLE `hash`.`customer_login_log`(
customer_id int UNSIGNED not null,
login_time TIMESTAMP,
login_ip int UNSIGNED,
login_type TINYINT NOT NULL
) PARTITION by hash(login_ip) PARTITIONS 6;--根据ip进行分区,不同的ip,分到的区不同

①、查看 customer_login_log 分区表物理结构:
在这里插入图片描述
②、customer_login_log 普通非分区表物理结构:
在这里插入图片描述
③、向HASH分区表customer_login_log中插入数据:

1
2
sql复制代码INSERT INTO customer_login_log(customer_id,login_time,login_ip,login_type)
VALUES (1,now(),11111,1);

④、查看分区表数据:
在这里插入图片描述

十、RANGE 分区表(按范围分区)

1、RANGE分区特点:

RANGE分区 是根据分区键值的范围把数据行存储到表的不同分区中,并且 多个分区的范围要连续,但是不能重叠。

注意: 默认情况下使用VALUES LESS THAN属性,即每个分区不包括指定的那个值

2、创建RANGE分区表:

1
2
3
4
5
6
7
8
9
10
11
12
sql复制代码create table `customer_login_log`(
`customer_id` int(10) UNSIGNED not null,
`login_time` TIMESTAMP not null,
`login_ip` int(10) UNSIGNED not null,
`login_type` TINYINT(4) NOT NULL
) ENGINE=INNODB
PARTITION BY RANGE( customer_id)(
PARTITION P0 VALUES LESS THAN (10000),
PARTITION P1 VALUES LESS THAN (20000),
PARTITION P2 VALUES LESS THAN (30000),
PARTITION P3 VALUES LESS THAN MAXVALUE
)

在这里插入图片描述
分区范围说明:

当插入的数据为30000到40000分区范围的数据时,没有创建分区范围为40000的分区的情况下,会返回错误提示;但,当存在图中p3分区的MAXVALUE这一分区时,所以没有指明分区范围的数据都会被插入到p3中

3、RANGE分区的使用场景

分区键为日期或是时间类型
所有查询中都包括分区键
定期按分区范围清理历史数据

十一、List分区(按分区键取值分区)

1、LIST分区的特点

定义: LIST分区按分区键取值的列表进行分区,并且同范围分区一样,各分区的列表只不能重复

注意:每一行数据必须能找到对应 分区列表,否则数据插入失败

2、创建LIST分区表:

1
2
3
4
5
6
7
8
9
10
sql复制代码create table `customer_login_log_list`(
`customer_id` int(10) UNSIGNED not null,
`login_time` TIMESTAMP not null,
`login_ip` int(10) UNSIGNED not null,
`login_type` TINYINT(4) NOT NULL
) ENGINE=INNODB
PARTITION BY LIST (login_type)(
PARTITION P0 VALUES IN (1,3,5,7,9),
PARTITION P1 VALUES IN (2,4,6,8)
)

插入包含未建立分区的分区键的值,会返回错误:

1
sql复制代码INSERT INTO customer_login_log_list(customer_id,login_time,login_ip,login_type)VALUES(100,now(),1,10)

错误截图:
在这里插入图片描述
根据login_type 的值进行分区 p0存储login_type为 1,3,5,7,9;p1存储login_type为2,4,6,8的数据,而插入的数据的login_type为10,不包含在p0或p1的login_type范围中,所以插入失败,返回错误提示。

十二、SQL执行计划及分页查询优化、分区键统计

1、执行计划分析

执行计划能告诉我们什么?

SQL如何使用索引
联接查询的执行顺序
查询扫描的数据行数
执行计划中的内容:

在这里插入图片描述

十三、执行计划内容的作用分析及示例

1、 ID列

执行计划中的id列的意义:

1
2
3
java复制代码ID列中的数据为一组数字,表示执行SELECT语句的顺序
ID值相同时,执行顺序由上至下
ID值越大优先级越高,越先被执行

查看执行计划:

1
2
3
4
5
6
7
8
sql复制代码EXPLAIN SELECT
c.`category_name`,
a.`product_name`,
b.`title`
FROM
product_info a
JOIN product_comment b ON a.`product_id` = b.`product_id`
JOIN product_category c ON c.`category_id` = a.`one_category_id`;

截图:在这里插入图片描述
复杂sql查看执行计划:

1
2
3
4
5
6
7
8
sql复制代码EXPLAIN
select title
from product_comment
WHERE product_id in (
SELECT max(product_id)
from product_info
WHERE one_category_id in (select min(category_id) from product_category)
)

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

1
java复制代码id为1,2,3

分组查询sql查看执行计划:

1
2
3
4
5
6
7
sql复制代码EXPLAIN
SELECT title
from (
SELECT one_category_id,max(product_id)AS pid
from product_info
GROUP BY one_category_id
) a JOIN product_comment b on a.pid = b.`product_id`

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

1
java复制代码id两个为1,一个为2

2、SELECT_TYPE列

在这里插入图片描述
执行计划案例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
sql复制代码EXPLAIN 
SELECT title
FROM product_comment
WHERE
product_id IN (
SELECT max(product_id)
FROM product_info
WHERE
one_category_id IN (
SELECT min(category_id)
FROM product_category
)
);

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

3、TABLE列

作用: 输出数据行所在的表的名称

1
2
sql复制代码<unionM,N>由ID为M,N查询union产生的结果集
<derivedN>/<subqueryN>由ID为N的查询产生的结果

执行计划案例1:

1
2
3
4
5
sql复制代码EXPLAIN
select product_category.`category_name`,product_info.`product_name`,product_comment.`title`
from product_info
JOIN product_comment on product_info.`product_id`=product_comment.`product_id`
JOIN product_category on product_category.`category_id`=product_info.`one_category_id`

在这里插入图片描述

4、PARTITIONS列

1
2
java复制代码作用: 对于分区表,显示查询的分区ID
对于非分区表,显示为NULL

执行计划案例:

1
2
3
4
sql复制代码EXPLAIN
SELECT *
FROM `crn`.`customer_login_log`
where customer_id = 1

在这里插入图片描述
以用户登录日志为例,应该使用用户表的用户id作为分区条件进行数据的存储和归档,这样有利于将同一个用户的所有数据写入到同一个分区区间,有利于避免查询登录日志时会对大表进行查询过程中对其他用户的登录日志进行过滤而导致的效率损耗!

5、TYPE列

在这里插入图片描述

6、Extra列

在这里插入图片描述

7、POSSIBLE_KEYS列

①指出MySQL能使用那些索引来优化查询

②查询列所涉及到的列上的索引都会被列出,但不一定会被使用

8、KEY列

①查询优化器优化查询实际所使用的索引

②如果没有可用的索引,则显示为NULL

③如查询使用了覆盖索引,则该索引仅出现在Key列中

9、KEY_LEN 列

①表示索引字段的最大长度

②Key_len的长度由字段定义计算而来,并非数据的实际长度

10、Ref列

表示哪些列或常量被用于查找索引列上的值

11、Rows列

①表示MySQL通过索引统计信息,估算的所需读取的行数

②Rows值的大小是个统计抽样结果,并不十分准确

12、Filtered列

①表示返回结果的行数占需读取行数的百分比

②Filtered列的值越大越好

③Filtered列的值依赖说统计信息

十四、执行计划的限制

1、无法展示存储过程,触发器,UDF对查询的影响

2、无法使用EXPLAIN对存储过程进行分析

3、早期版本的MySQL只支持对SELECT语句进行分析

十五、优化分页查询示例

需求: 根据audit_status及product_id 创建联合索引,这里需要明确哪一个值放在联合索引的左侧,使用product_id作为索引放在组合索引左侧是最合适的。

创建执行计划:

1
2
3
4
5
6
sql复制代码EXPLAIN
SELECT customer_id,title,content
from product_comment
where audit_status=1
AND product_id=199727
limit 0,5

在这里插入图片描述
初步优化,创建联合索引:

1
sql复制代码CREATE INDEX idx_productID_auditStats on product_comment(product_id,audit_status)

经过添加索引优化后的执行计划执行结果:
在这里插入图片描述
其查询效率明显提高,由type列可知,由原来的ALL进行全表扫描查询降为非唯一索引查询。

进一步优化分页查询

1
2
3
4
5
6
7
sql复制代码SELECT t.customer_id,t.title,t.content
from (
SELECT `comment_id`
from product_comment
where product_id=199727 AND audit_status=1 LIMIT 0,5
) a JOIN product_comment t
ON a.comment_id = t.comment_id;

优化说明: 先通过分页查询获取到对应数据的comment_id,此时的查询不会对其他字段进行查询返回,默认可以通过主键索引进行查询,效率极高;然后再讲查询到的a.commetn_id作为临时子表再与product_comment进行comment_id的匹配查询,此时直接通过comment_id进行查询返回包含comment_id在内的其他的字段。这种查询方式在IO上能节约很多的资源,当数据量上万时,效率依然不会受到太大影响。

十六、如何删除重复数据

删除评论表中对同一订单同一商品的重复评论,只保留最早的一条

1、步骤一:查看是否存在对于一订单同一商品的重复评论

1
2
3
sql复制代码select order_id,product_id,COUNT(*)
from product_comment
GROUP BY order_id,product_id HAVING COUNT(*)>1

2、步骤二:备份product_comment表

创建备份表:

1
2
sql复制代码CREATE TABLE `mc_productdb`.bak_product_comment_200815
LIKE `mc_productdb`.product_comment;

同步表数据:

1
2
sql复制代码INSERT INTO `mc_productdb`.`bak_product_comment_200815`
select * from `mc_productdb`.`product_comment`;

3、步骤三:删除同一订单的重复评论

1
2
3
4
5
6
7
8
9
sql复制代码DELETE a
FROM product_comment a
JOIN (
SELECT order_id,product_id,MIN(comment_id) AS comment_id
FROM product_comment
GROUP BY order_id,product_id
HAVING COUNT(*)>=2
) b ON a.order_id=b.order_id AND a.product_id=b.product_id
AND a.comment_id> b.comment_id

十七、进行分区间统计

需求:统计消费总金额大于1000元的,800到1000元的,500到800元的,以及500元以下的人数

1
2
3
4
5
6
7
8
9
10
11
sql复制代码SELECT count(CASE WHEN IFNULL(total_money,0) >=1000 THEN a.customer_id END) AS '大于1000'
,count(CASE WHEN IFNULL(total_money,0) >=800 AND IFNULL(total_money,0)<1000
THEN a.customer_id END) AS '800-1000'
,count(CASE WHEN IFNULL(total_money,0) >=800 AND IFNULL(total_money,0)<800
THEN a.customer_id END) AS '500-800'
,count(CASE WHEN IFNULL(total_money,0) <500 THEN a.customer_id END) '小于500'
from mc_userdb.customer_login a
LEFT JOIN
( SELECT customer_id,SUM(order_money) AS total_money
from mc_orderdb.`order_master` GROUP BY customer_id) b
ON a.customer_id=b.customer_id

在这里插入图片描述

十八、捕获有问题的SQL

核心:利用执行计划优化查询

如何找到需要优化的SQL呢? 答案:慢查询日志

1
2
3
4
5
6
7
8
9
10
11
12
13
sql复制代码启用mysql慢查日志

set global slow_query_log_file = /sql_log/slow_log.log;

set global log_queries_not_using_indexes = on;

未使用索引的SQL记录日志

set global long_query_time = 0.001;

抓取执行超过多少时间的SQL(秒)

set global low_query_log = on;

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

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring5源码解析四

发表于 2021-08-06
  • instantiateBean(beanName, mbd); 使用默认构造方法实例化对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
java复制代码protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
//getInstantiationStrategy()得到类的实例化策略
//默认情况下是得到一个反射的实例化策略
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}

# SimpleInstantiationStrategy
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
//检测 bean 配置中是否配置了 lookup-method 或 replace-method
//如果配置了就需使用 CGLIB 构建 bean 对象
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
//得到默认的无参构造方法
//constructorToUse:表示spring使用哪个构造方法实例化对象
constructorToUse = clazz.getDeclaredConstructor();
}
//记录resolvedConstructorOrFactoryMethod为默认的无参构造方法
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
//调用反射技术,实例化对象
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
// 设置构造方法为可访问
ReflectionUtils.makeAccessible(ctor);
//反射创建对象 ctor.newInstance(args)
return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
  • autowireConstructor(beanName, mbd, ctors, args); 使用有参构造方法实例化对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
java复制代码protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
//实力一个BeanWrapperImpl 对象很好理解
//前面外部返回的BeanWrapper 其实就是这个BeanWrapperImpl
//因为BeanWrapper是个接口
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);

//决定要使用哪个构造方法实例化对象
Constructor<?> constructorToUse = null;

//构造方法的值,注意不是参数
//我们都知道构造方法通过反射来实例一个对象
//在调用反射来实例对象的时候,需要具体的值
//这个变量就是用来记录这些值的
//但是这里需要注意的是argsHolderToUse是一个数据结构
ArgumentsHolder argsHolderToUse = null;

//argsToUse[]才是真正的值
Object[] argsToUse = null;

//确定参数值列表
//argsToUse可以有两种办法设置
//第一种通过beanDefinition设置
//第二种通过xml设置
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
//获取已解析的构造方法
//一般不会有,因为构造方法一般会提供一个
//除非有多个。那么才会存在已经解析完成的构造方法
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached constructor...
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
}

if (constructorToUse == null) {
//如果没有已经解析的构造方法
//则需要去解析构造方法
//判断构造方法是否为空,判断是否根据构造方法自动注入
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;

//定义了最小参数个数 minNrOfArgs
//如果你给构造方法的参数列表给定了具体的值
//那么这些值得个数就是构造方法参数的个数
int minNrOfArgs;
if (explicitArgs != null) {
//如果传递过来的参数不为null,那就以传递过来的参数个数作为“最小参数个数”
minNrOfArgs = explicitArgs.length;
}
else {
//mybatis:mbd.getConstructorArgumentValues().addGenericArgumentValue("com.index.dao");
//实例一个对象,用来存放构造方法的参数值
//当中主要存放了参数值和参数值所对应的下表
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
/**
* 确定构造方法参数数量,假设有如下配置:
* <bean id="llsydn" class="com.llsydn.Luban">
* <constructor-arg index="0" value="str1"/>
* <constructor-arg index="1" value="1"/>
* <constructor-arg index="2" value="str2"/>
* </bean>
*
* 在通过spring内部给了一个值的情况,那么表示你的构造方法的“最小参数个数”是确定的
*
* minNrOfArgs = 3
*/
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}

// Take specified constructors, if any.
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
//怎么排序的呢?
//1.根据构造方法的访问权限级别 public -- protected -- private
//2.根据构造方法的参数数量进行排序 从多到小
/**
* 有限反问权限,继而参数个数
* 这个自己可以写个测试去看看到底是不是和我说的一样
* 1. public Luban(Object o1, Object o2, Object o3)
* 2. public Luban(Object o1, Object o2)
* 3. public Luban(Object o1)
* 4. protected Luban(Integer i, Object o1, Object o2, Object o3)
* 5. protected Luban(Integer i, Object o1, Object o2)
* 6. protected Luban(Integer i, Object o1)
*/
AutowireUtils.sortConstructors(candidates);
//定义了一个差异变量,这个变量很有分量,后面有注释
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null; //记录异常的构造方法(构造方法差异值一样,spring不知怎么选择)
LinkedList<UnsatisfiedDependencyException> causes = null;

//循环所有的构造方法
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
/**
* 这个判断别看只有一行代码理解起来很费劲
* 首先constructorToUse != null这个很好理解
* 前面已经说过首先constructorToUse主要是用来装已经解析过了并且在使用的构造方法
* 只有在他等于空的情况下,才有继续的意义,因为下面如果解析到了一个符合的构造方法
* 就会赋值给这个变量(下面注释有写)。故而如果这个变量不等于null就不需要再进行解析了,说明spring已经
* 找到一个合适的构造方法,直接使用便可以
* argsToUse.length > paramTypes.length这个代码就相当复杂了
* 首先假设 argsToUse = [1,"llsydn",obj]
* 那么回去匹配到上面的构造方法的1和5
* 由于构造方法1有更高的访问权限,所有选择1,尽管5看起来更加匹配
* 但是我们看2,直接参数个数就不对所以直接忽略
*
*/
if (constructorToUse != null && argsToUse.length > paramTypes.length) {
// Already found greedy constructor that can be satisfied ->
// do not look any further, there are only less greedy constructors left.
break;
}
//如果遍历的当前构造方法的参数类型的长度,不等于最小的参数格式:证明不能用该构造方法实例化对象
if (paramTypes.length < minNrOfArgs) {
continue;
}

ArgumentsHolder argsHolder;
if (resolvedValues != null) {
try {
//判断是否加了ConstructorProperties注解如果加了则把值取出来
//可以写个代码测试一下
//@ConstructorProperties(value = {"xxx", "111"})
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
//获取构造方法参数名称列表
/**
* 假设你有一个(String llsydn, Object zilu)
* 则paramNames=[llsydn,zilu]
*/
paramNames = pnd.getParameterNames(candidate);
}
}

//获取构造方法参数值列表
/**
* 这个方法比较复杂
* 因为spring只能提供字符串的参数值
* 故而需要进行转换
* argsHolder所包含的值就是转换之后的
*
* 例如:在xml配置文件设置了“order”这是一个字符串,要转成order对象
*/
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring);
}
catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
}
// Swallow and try next constructor.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
}
else {
// Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}

/**
* typeDiffWeight 差异量,何谓差异量呢?
* argsHolder.arguments和paramTypes之间的差异
* 每个参数值得类型与构造方法参数列表的类型直接的差异
* 通过这个差异量来衡量或者确定一个合适的构造方法
*
* 值得注意的是constructorToUse=candidate
*
* 第一次循环一定会typeDiffWeight < minTypeDiffWeight,因为minTypeDiffWeight的值非常大
* 然后每次循环会把typeDiffWeight赋值给minTypeDiffWeight(minTypeDiffWeight = typeDiffWeight)
* else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight)
* 第一次循环肯定不会进入这个
* 第二次如果进入了这个分支代表什么?
* 代表有两个构造方法都符合我们要求?那么spring有迷茫了(spring经常在迷茫)
* spring迷茫了怎么办?
* ambiguousConstructors.add(candidate);
* 顾名思义。。。。
* ambiguousConstructors=null 非常重要?
* 为什么重要,因为需要清空
* 这也解释了为什么他找到两个符合要求的方法不直接抛异常的原因
* 如果这个ambiguousConstructors一直存在,spring会在循环外面去exception
* 很牛逼呀!!!!
*/
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
if (typeDiffWeight < minTypeDiffWeight) {
//第一遍100%会进这里;当找到差异值更小的,就将异常清空。
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
//清空异常的构造器
ambiguousConstructors = null;
}
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
//这里表示,找到了差异值一样的构造参数,spring不知道怎么选择,就先记录在ambiguousConstructors。
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
//循环结束
//没有找打合适的构造方法
if (constructorToUse == null) {
if (causes != null) {
UnsatisfiedDependencyException ex = causes.removeLast();
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
throw ex;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Could not resolve matching constructor " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
}

//如果ambiguousConstructors还存在则异常?为什么会在上面方法中直接exception?
//上面注释当中有说明
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous constructor matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
ambiguousConstructors);
}

if (explicitArgs == null) {
/*
* 缓存相关信息,比如:
* 1. 已解析出的构造方法对象 resolvedConstructorOrFactoryMethod
* 2. 构造方法参数列表是否已解析标志 constructorArgumentsResolved
* 3. 参数值列表 resolvedConstructorArguments 或 preparedConstructorArguments
* 这些信息可用在其他地方,用于进行快捷判断
*/
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
try {
/*
* 使用反射创建实例 lookup-method 通过CGLIB增强bean实例
*/
final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
Object beanInstance;

if (System.getSecurityManager() != null) {
final Constructor<?> ctorToUse = constructorToUse;
final Object[] argumentsToUse = argsToUse;
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
beanFactory.getAccessControlContext());
}
else {
//最后:通过找到constructorToUse构造方法,进行实例化对象
beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
}
bw.setBeanInstance(beanInstance);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via constructor failed", ex);
}
}

autowireConstructor使用特殊构造方法实例化对象,主要做了几步操作:
1.判断要用哪个构造方法实例化对象:constructorToUse,那些参数:argsToUse
2.先决定构造方法的最小参数个数:minNrOfArgs。
3.对所有的构造方法进行排序,排序规则:访问权限级别(public–protected–private) 和 参数数量(从多到小)
4.遍历所有构造方法,根据“最小参数个数”minNrOfArgs去判断构造方法是否符合
5.计算构造方法的“typeDiffWeight 差异量”,找到最小的差异量,spring就用这个构造方法实例化对象。
6.如果spring找到了多个差异量一样的,也符合最小参数个数的构造方法,就记录在ambiguousConstructors。
7.如果这个ambiguousConstructors一直存在,spring会在循环外面去exception
8.如果这个ambiguousConstructors为null,spring就会缓存找到的constructorToUse的构造方法
9.最后通过找到constructorToUse构造方法,进行实例化对象。

1.instanceWrapper = createBeanInstance(beanName, mbd, args);

至此,使用构造方法实例化对象的方法就解析到这里。创建的对象,就执行下面这步

2.addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这个很重要,主要将刚new的对象,存放在singletonFactories对象。主要是用来解决循环依赖。

3.populateBean(beanName, mbd, instanceWrapper);
但是这个时候,对象还没进行属性赋值,需要调用该方法进行属性赋值

  • populateBean(beanName, mbd, instanceWrapper);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
java复制代码protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
//是否需要属性赋值
boolean continueWithPropertyPopulation = true;

//实现InstantiationAwareBeanPostProcessor接口,就可以不进行属性赋值,返回一个寡对象
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}

//属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

//判断属性自动装配模型(NO)
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}

//是否需要处理InstantiationAwareBeanPostProcessors
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
//是否需要深度检查(循环引用)
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

if (hasInstAwareBpps || needsDepCheck) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
//拿到所有get和set的方法
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}

if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}

这里进行属性赋值,主要是调用了两个BeanPostProcessor进行属性赋值。
1.AutowiredAnnotationBeanPostProcessor(处理@Autowired注解)
2.CommonAnnotationBeanPostProcessor(处理@Resource、@PostConstruct和@PreDestroy注解)

  • AutowiredAnnotationBeanPostProcessor源码解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
java复制代码// @Autowired实现属性注入
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

//找出类中被@Autowired注解的属性和方法
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
//属性的注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
//拿到需要注入的属性
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
//遍历一个一个注入属性
for (InjectedElement element : elementsToIterate) {
if (logger.isDebugEnabled()) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
//属性注入
element.inject(target, beanName, pvs);
}
}
}

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
//拿到要注入的属性
Field field = (Field) this.member;
Object value;
if (this.cached) {
//如果被缓存了,从缓存里面拿
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//从BeanFactory中转换这个依赖(重要)
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();

//这里isTypeMatch,就是从earlySingletonObjects中拿出“自动装配的bean”的类型是否相同
//如果类型相同,就真正的set值field.set(bean, value);
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}

@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
//判断依赖的类型
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
//真正干活的方法,从BeanFactory中转换这个依赖
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}

Class<?> type = descriptor.getDependencyType();
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}

Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}

String autowiredBeanName;
Object instanceCandidate;

if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}

if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
//处理自动装配属性,进行转换。@Autowired 重要
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
//再次调用了getBean方法
return beanFactory.getBean(beanName);
}

进行属性赋值,就可能会出现循环依赖的情况:
例如:A类有一个B类的属性,B类有一个A类的属性。
那么在属性填充的时候,就会出现在创建A对象的时候,然后自动装配B,那么spring也会去创建B对象。那么这个时候,就会有一个循环依赖的情况。
那么spring是怎么解决循环依赖的?

  • 在处理循环依赖的情况,spring主要用了3个map,1个list,最重要的就是singletonFactories
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码//这个时候bean已经被创建出来,但是还没进行属性装配
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//singletonObjects是完整的对象
if (!this.singletonObjects.containsKey(beanName)) {
//已经被new出来,但是属性没有被填充,主要是解决循环依赖
this.singletonFactories.put(beanName, singletonFactory);
//已经被new出来,但是属性没有被填充,主要是作为类型校验
this.earlySingletonObjects.remove(beanName);
//在registeredSingletons记录一下
this.registeredSingletons.add(beanName);
}
}
}

//进行属性赋值的时候,调用该方法,从singletonFactories中拿到那些依赖的bean,进行属性赋值
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从map中获取bean如果不为空直接返回,不再进行初始化工作
//讲道理一个程序员提供的对象这里一般都是为空的
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

至此,spring对bean的实例化过程,就解析到这里了。
下面上spring实例化过程的流程图:

  • 在这里插入图片描述

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring5源码解析三

发表于 2021-08-06

Spring5源码解析(bean实例化)

在上一边的博客里面已经介绍:spring在bean扫描过程,已经将所有的bean保存在一个beanDefinitionMap的map对象中,但是这些bean还是一个beanDefinition,还没进行实例化的,所以这次就重点解析bean的实例化过程。
bean扫描过程源码分析:blog.csdn.net/qq_24101357…

  • finishBeanFactoryInitialization(beanFactory);
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 合并父类,RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 举个例子说明一下,不怎么重要:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码<bean id="parentBd" class="com.llsydn.merge.PcBd">
<property name="name" value="parent"></property>
</bean>

<bean id="childBd" parent="parentBd">
<property name="name" value="children"></property>
</bean>

public class PcBd {
private String name;
public void setName(String name) {
this.name = name;
}
public void test() {
System.out.println(this.name);
}
}

public class test{
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();

PcBd parentBd = (PcBd) ac.getBean("parentBd");
parentBd.test();

PcBd childBd = (PcBd) ac.getBean("childBd");
childBd.test();
}
}
// 输出:
parent
children

上面的代码,主要是实现bean实例化的准备过程,从beanDefinitionNames中拿到被扫描出来的所有beanName。
然后遍历beanDefinitionNames,一个一个的调用getBean()方法,实例化这些bean对象。

  • getBean(beanName)
  • 在这里插入图片描述
  • isSingletonCurrentlyInCreation 以为还没有需要创建对象的时候。

spring自动装配的模型 不等于 自动装配的技术。

  1. no 会使用 bytype技术
  2. bean的默认自动装配模型 == no
1
2
3
4
5
6
7
8
9
10
11
12
> kotlin复制代码public class OrderService{
> IndexService indexService;
> }
> 这个indexService,直接忽略了装配
>
> public class OrderService{
> @Autowired
> IndexService indexService;
> }
> 这个indexService会先使用bytype技术自动装配,找不到再使用byname自动装配
>
>
  • 下面进入到doGetBean()进行分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
java复制代码protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
/**
* 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两个原因
* 1、name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean
* 实现类所创建的 bean。在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储
* 方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要
* 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。
* 2、还是别名的问题,转换需要
*/
final String beanName = transformedBeanName(name);
Object bean;

/**
* 这个方法在初始化的时候会调用,在getBean的时候也会调用
* 为什么需要这么做呢?
* 也就是说spring在初始化的时候先获取这个对象
* 判断这个对象是否被实例化好了(普通情况下绝对为空====有一种情况可能不为空lazy=true,第二次调用)
* 从spring的bean容器中获取一个bean,由于spring中bean容器是一个map(singletonObjects)
* 所以你可以理解getSingleton(beanName)等于beanMap.get(beanName)
* 由于方法会在spring环境初始化的时候(就是对象被创建的时候调用一次)调用一次
* 还会在getBean的时候调用一次
* 所以再调试的时候需要特别注意,不能直接断点在这里,
* 需要先进入到annotationConfigApplicationContext.getBean(IndexDao.class)
* 之后再来断点,这样就确保了我们是在获取这个bean的时候调用的
*
* 需要说明的是在初始化时候调用一般都是返回null
*
* 第一次调用:isSingletonCurrentlyInCreation以为还没到开始创建对象的时候
*/
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 这里的代码是对于日志的记录,方便我们以后阅读应该注释,不影响spring功能
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}

/**
* 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
*/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
/**
* 原型,判断当前正在创建的bean是不是原型。
* 如果是原型不应该在初始化的时候创建
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}

// Check if bean definition exists in this factory. 父工厂
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}

if (!typeCheckOnly) {
//添加到alreadyCreated set集合当中,表示他已经创建过一场
markBeanAsCreated(beanName);
}

try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}

// 创建单例的bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}

// Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
  • createBean(beanName, mbd, args); 分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
java复制代码@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {

if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;

// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}

// Prepare method overrides.
// 处理 lookup-method 和 replace-method 配置,Spring 将这两个配置统称为 override method
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}

try {
// 在 bean 初始化前应用后置处理,如果后置处理返回的 bean 不为空,则直接返回
// 这个类需要通过代码演示
// 这个主要的作用是,将bean的所有依赖去到,直接返回一个寡对象。实现InstantiationAwareBeanPostProcessor接口。
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
// 调用doCreateBean 创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
  • Object beanInstance = doCreateBean(beanName, mbdToUse, args); 分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
java复制代码protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean. 包装类
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/**
* 创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回。
* createBeanInstance中包含三种创建 bean 实例的方式:
* 1. 通过工厂方法创建 bean 实例
* 2. 通过构造方法自动注入(autowire by constructor)的方式创建 bean 实例
* 3. 通过无参构造方法方法创建 bean 实例
*
* 若 bean 的配置信息中配置了 lookup-method 和 replace-method,则会使用 CGLIB
* 增强 bean 实例。关于lookup-method和replace-method后面再说。
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//拿到包装类的原生对象
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//非常重要
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// Initialize the bean instance.
Object exposedObject = bean;
try {
//设置属性,非常重要
populateBean(beanName, mbd, instanceWrapper);
//执行后置处理器,aop就是在这里完成的处理
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}

if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}

doCreateBean()方法主要的作用:
1.先利用反射技术生成bean的实例对象。createBeanInstance(beanName, mbd, args)
2.对实例对象的属性,进行自动装配。populateBean(beanName, mbd, instanceWrapper);
3.执行后置处理器,进行bean增强。 initializeBean(beanName, exposedObject, mbd);

  • createBeanInstance(beanName, mbd, args) 利用反射技术实例化对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
java复制代码protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);

/**
* 检测一个类的访问权限spring默认情况下对于非public的类是允许访问的。
*/
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}

Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}

/**
*
* 如果工厂方法不为空,则通过工厂方法构建 bean 对象
* 这种构建 bean 的方式可以自己写个demo去试试
* 源码就不做深入分析了,有兴趣的同学可以和我私下讨论
* 如果设置了factoryMethod,就使用FactoryMethod方法实例化对象。(举例子实现)
*/
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}

// Shortcut when re-creating the same bean...
/**
* 从spring的原始注释可以知道这个是一个Shortcut,什么意思呢?快捷方式
* 当多次构建同一个 bean 时,可以使用这个Shortcut,
* 也就是说不在需要次推断应该使用哪种方式构造bean
* 比如在多次构建同一个prototype类型的 bean 时,就可以走此处的shortcut
* 这里的 resolved 和 mbd.constructorArgumentsResolved 将会在 bean 第一次实例化的过程中被设置
*/
boolean resolved = false;
boolean autowireNecessary = false; //是否必须要自动装配
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
//如果已经解析了构造方法的参数,则必须要通过一个带参构造方法来实例
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
//通过构造方法自动装配的方式构造 bean 对象
return autowireConstructor(beanName, mbd, null, null);
}
else {
//通过默认的无参构造方法进行
return instantiateBean(beanName, mbd);
}
}

//1.当有无参构造方法,就默认使用该无参构造方法,实例化对象。
//因为spring不知道,你是使用有参构造方法实例化对象,还是使用无参构造方法实例化对象,所以就简单一点,使用无参构造方法实例化对象。
//2.当且仅当只有一个有参构造方法,就使用该有参构造方法,实例化对象。
//当你有多个有参构造方法的时候,spring也是不知道该用哪个构造方法实例化对象,所以就简单一点,使用无参构造方法实例化对象。
//由后置处理器决定返回哪些构造方法
//拿到bean的构造方法,然后使用该构造方法实例化对象。(要用哪个构造方法实例化对象,交给BeanPostProcessors处理)
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
//使用默认的无参构造方法进行初始化
return instantiateBean(beanName, mbd);
}

createBeanInstance主要的步骤有:
1.判断bean是否可以访问。
2.判断bean是否有factory-method方法,如果有就调用factory-method方法实例化对象
3.判断bean是否是第二次实例化,并是否存在快捷方式实例化对象。
4.判断bean的构造方法:

1.当bean有无参构造方法,就默认使用该无参构造方法,实例化对象
2.当bean只有一个有参构造方法,就使用该有参构造方法,实例化对象
3.当bean有多个有参构造方法,使用无参构造方法,实例化对象。(spring无法判断要用那个构造方法)

备注:spring是使用构造方法方式,实例化对象

  • determineConstructorsFromBeanPostProcessors() 用哪个构造方法实例化对象,交给BeanPostProcessors处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
java复制代码protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}

#AutowiredAnnotationBeanPostProcessor
//决定使用哪个候选的构造方法
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {

// Let's check for lookup methods here..
if (!this.lookupMethodsChecked.contains(beanName)) {
try {
ReflectionUtils.doWithMethods(beanClass, method -> {
Lookup lookup = method.getAnnotation(Lookup.class);
if (lookup != null) {
Assert.state(this.beanFactory != null, "No BeanFactory available");
LookupOverride override = new LookupOverride(method, lookup.value());
try {
RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);
mbd.getMethodOverrides().addOverride(override);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(beanName,
"Cannot apply @Lookup to beans without corresponding bean definition");
}
}
});
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
}
this.lookupMethodsChecked.add(beanName);
}

// 候选构造器
// Quick check on the concurrent map first, with minimal locking.
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// Fully synchronized resolution now...
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// 拿到bean的所有构造方法
Constructor<?>[] rawCandidates;
try {
rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;
for (Constructor<?> candidate : rawCandidates) {
if (!candidate.isSynthetic()) {
//记录bean有多少个构造方法
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;
}
AnnotationAttributes ann = findAutowiredAnnotation(candidate);
if (ann == null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
if (ann != null) {
if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
boolean required = determineRequiredStatus(ann);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
requiredConstructor = candidate;
}
candidates.add(candidate);
}
//如果构造方法的没有参数,就是默认的构造方法。就使用该默认构造方法实例化对象。
else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null) {
if (defaultConstructor != null) {
candidates.add(defaultConstructor);
}
else if (candidates.size() == 1 && logger.isWarnEnabled()) {
logger.warn("Inconsistent constructor declaration on bean with name '" + beanName +
"': single autowire-marked constructor flagged as optional - " +
"this constructor is effectively required since there is no " +
"default constructor to fall back to: " + candidates.get(0));
}
}
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
//只有一个有参构造方法,返回该有参构造方法
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {

candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
else {
//返null,即使用默认无参构造方法实例化对象。
candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Feign调用全局异常处理解决

发表于 2021-08-06

异常信息形如:

TestService#addRecord(ParamVO) failed and no fallback available.;

对于failed and no fallback available.这种异常信息,是因为项目开启了熔断:

feign.hystrix.enabled: true
当调用服务时抛出了异常,却没有定义fallback方法,就会抛出上述异常。由此引出了第一个解决方式。

解决方案:

自定义Feign解析器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
java复制代码import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.crecgec.baseboot.jsoncore.exception.BaseException;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class FeignErrorDecoder implements ErrorDecoder {

@Override
public Exception decode(String methodKey, Response response) {
try {
// 这里直接拿到我们抛出的异常信息
String message = Util.toString(response.body().asReader());
try {
JSONObject jsonObject = JSONObject.parseObject(message);
return new BaseException(jsonObject.getString("resultMsg"), jsonObject.getInteger("resultCode"));
} catch (JSONException e) {
e.printStackTrace();
}

} catch (IOException ignored) {
}
return decode(methodKey, response);
}
}

定义系统的异常类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
java复制代码public class BaseException extends RuntimeException {
private int status ;

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public BaseException() {
}

public BaseException(String message, int status) {
super(message);
this.status = status;
}

public BaseException(String message) {
super(message);
}

public BaseException(String message, Throwable cause) {
super(message, cause);
}

public BaseException(Throwable cause) {
super(cause);
}

public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

统一异常拦截转换对应的异常信息返回前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
java复制代码public class ResultSet {

/**
* 返回的状态码
*/
private Integer resultCode;

/**
* 返回的消息
*/
private String resultMsg;

/**
* 返回的数据
*/
private Object data;

public ResultSet() {
}

public ResultSet(Integer resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}

public ResultSet(Integer resultCode, String resultMsg, Object data) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
this.data = data;
}

public Integer getResultCode() {
return resultCode;
}

public void setResultCode(Integer resultCode) {
this.resultCode = resultCode;
}

public String getResultMsg() {
return resultMsg;
}

public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}
}

全局异常类处理配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码@ExceptionHandler(value = BaseException.class)
public ResultSet defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, BaseException e) {
ResultSet resultSet = new ResultSet();

if (e.getStatus() == 400) {
resultSet.setResultCode(-1);
resultSet.setResultMsg(e.getMessage());
resultSet.setData(null);
resp.setStatus(400);
} else {
resp.setStatus(500);
if(logger.isErrorEnabled()){
logger.error("系统异常,请联系系统开发人员进行处理", e);
}
resultSet.setResultCode(-1);
resultSet.setResultMsg(e.getMessage());
resultSet.setData(null);
}

return resultSet;
}

这样就能完成了feign接收异常处理的自定义异常信息!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring5源码解析二 ConfigurationClas

发表于 2021-08-06

//上面的是扫描普通的bean

//处理@Import的3种情况(正常类;ImportSelector类;ImportBeanDefinitionRegistrar类)
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
kotlin复制代码if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//反射实现一个对象
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
//回调
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
//递归,这里第二次调用processImports
//如果是一个普通类,会斤else
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
//添加到一个list当中和importselector不同
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 否则,加入到importStack后调用processConfigurationClass 进行处理
//processConfigurationClass里面主要就是把类放到configurationClasses
//configurationClasses是一个集合,会在后面拿出来解析成bd继而注册
//可以看到普通类在扫描出来的时候就被注册了
//如果是importSelector,会先放到configurationClasses后面进行出来注册
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process imports candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}

}

1
2
3
4
5
6
7
8
9
arduino复制代码
>processImports,处理import的类,主要是处理3种。
>1. 普通的bean
>2. 实现了 importSelector的bean(可以注册多个类的String数组字符串)
>3. 实现了ImportBeanDefinitionRegistrar

>1. import普通的bean,会先加入到importStack后调用processConfigurationClass 进行处理,放入到configurationClasses后面进行出来注册。
>2. import的是importSelector,会先实例化这个ImportSelector 类,然后执行该selector的selectImports方法,获取要注入到spring的bean工厂的类数组字符串,然后 对该String[]数组,进行再一次调用processImports方法处理,最终都是会进入到import普通bean的步骤,即:会先加入到importStack后调用processConfigurationClass 进行处理,放入到configurationClasses后面进行出来注册。
>3. import的是ImportBeanDefinitionRegistrar,会先实例化这个ImportBeanDefinitionRegistrar类,然后将它添加到一个list当中和importselector不同。

对于import的bean处理,是在 ConfigurationClassPostProcessor # processConfigBeanDefinitions()方法处理,即下面代码:

/**

  • 这里值得注意的是扫描出来的bean当中可能包含了特殊类
  • 比如ImportBeanDefinitionRegistrar那么也在这个方法里面处理
  • 但是并不是包含在configClasses当中
  • configClasses当中主要包含的是importSelector
  • 因为ImportBeanDefinitionRegistrar在扫描出来的时候已经被添加到一个list当中去了
    */
    //bd 到 map 除却普通
    this.reader.loadBeanDefinitions(configClasses);

//ConfigurationClassBeanDefinitionReader
public void loadBeanDefinitions(Set configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}

private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
scss复制代码if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}

//如果一个类是被import的,会被spring标准
//早这里完成注册
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//@Bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}

//xml
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

//注册Registrar
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

}

//将import和importSelector注入的bean,注册到bean工厂中
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

1
2
3
4
5
6
7
8
9
10
11
12
13
ini复制代码ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);

if (logger.isDebugEnabled()) {
logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
}

}

//将ImportBeanDefinitionRegistrar注入的bean,注册到bean工厂中
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
shell复制代码> 总结:spring扫描类分了4种情况
> 1. 普通类,扫描到即刻注册到beanDefinitionMap中。
> 2. import(普通类),扫描到先存放到configurationClasses一个map集合。
> 3. import(importSelector),扫描到遍历循环一个一个的放到configurationClasses一个map集合。
> 4. import(importBeanDefinitionRegistrar),扫描到之后存放到importBeanDefinitionRegistrars一个map集合中。
> 备注:对于import的类,spring是在扫描之后,调用了loadBeanDefinitions方法将扫描到import的类注册到beanDefinitionMap中。

> full和lite的主要作用:
> 1. 当一个bean加了@Configuration注解,即是full全注解类。
> 2. 当一个bean加了@Component、@ComponentScan、@Import、@ImportResource等注解,即是lite
>备注:full全注解类,spring会为该类生成一个cglib代理类。

> 对于加了@Configuration注解的类,spring会对该类使用cglib产生一个代理类。
> 为什么需要使用cglib代理?
> > AppConfig类,使用@Bean注入了两个bean,在configDao2()方法中调用了configDao1()方法,这里就是为了防止configDao1对象被创建两次的问题。
>
> 备注:cglib是基于继承的。
> 是在invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);方法执行的。

private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
//因为只有一条数据ConfigurationClassPostProcessor
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
“postProcessBeanFactory already called on this post-processor against “ + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported…
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//给配置类产生cglib代理
//为什么需要产生cglib代理?(给AppConfig产生代理类,configDao2()方法调用了configDao1()方法)
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//判断是否是一个全注解类
//扫描是全注解类?full和lite的关系
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException(“”);
}
else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
logger.warn(“”);
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
return;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
scss复制代码//完成cglib代理
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
//完成对全注解类的cglib代理
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isDebugEnabled()) {
logger.debug(""));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
}

}

ConfigurationClassEnhancer

public Class enhance(Class configClass, @Nullable ClassLoader classLoader) {
//判断是否被代理过
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
if (logger.isDebugEnabled()) {
logger.debug(“”);
}
return configClass;
}
//没有被代理cglib代理
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isDebugEnabled()) {
logger.debug(String.format(“”);
}
return enhancedClass;
}

private Enhancer newEnhancer(Class configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
//增强父类,地球人都知道cglib是基于继承来的
enhancer.setSuperclass(configSuperClass);
//增强接口,为什么要增强接口?
//便于判断,表示一个类以及被增强了(实现BeanFactoryAware,为了获得BeanFactory对象)
enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class});
//不继承Factory接口
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// BeanFactoryAwareGeneratorStrategy是一个生成策略
// 主要为生成的CGLIB类中添加成员变量$$beanFactory
// 同时基于接口EnhancedConfiguration的父接口BeanFactoryAware中的setBeanFactory方法,
// 设置此变量的值为当前Context中的beanFactory,这样一来我们这个cglib代理的对象就有了beanFactory
// 有了factory就能获得对象,而不用去通过方法获得对象了,因为通过方法获得对象不能控制器过程
// 该BeanFactory的作用是在this调用时拦截该调用,并直接在beanFactory中获得目标bean
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
//过滤方法,不能每次都去new
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}

//CALLBACK_FILTER:setCallbackFilter
private static final Callback[] CALLBACKS = new Callback[] {
//增强方法,主要控制bean的作用域
//(不用每一次都去调用new)
new BeanMethodInterceptor(),
//设置一个beanFactory
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

/**

  • 用于拦截@Bean方法的调用,并直接从BeanFactory中获取目标bean,而不是通过执行方法。
    */
    private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {

@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
scss复制代码  // enhancedConfigInstance 代理
// 通过enhancedConfigInstance中cglib生成的成员变量$$beanFactory获得beanFactory。
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

// Determine whether this bean is a scoped-proxy
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}

if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}

//一个非常牛逼的判断
//判断到底是new 还是get
//判断执行的方法和调用方法是不是同一个方法
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
//如果是同一个方法:调用代理对象的方法,执行被代理类的方法(即configDao1()的new ConfigDao1()实例化configDao1对象)
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
//不是同一个方法:调用getBean方法。(即在执行configDao2()方法,调用了configDao1()方法)
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);

}

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
//判断他是否正在创建
boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
try {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, false);
}
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
if (useArgs && beanFactory.isSingleton(beanName)) {
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
//beanFactory.getBean
//这个方法spring就写的非常牛逼,在bean实例化的会重点解析
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName));
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
if (beanInstance.equals(null)) {
if (logger.isDebugEnabled()) {
logger.debug(“”));
}
beanInstance = null;
}
else {
String msg = “”;
try {
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
msg += “ Overriding bean of same name declared in: “ + beanDefinition.getResourceDescription();
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore - simply no detailed message then.
}
throw new IllegalStateException(msg);
}
}
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
if (currentlyInvoked != null) {
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
beanFactory.registerDependentBean(beanName, outerBeanName);
}
return beanInstance;
}
finally {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}

1
2
3
4
5
6
scss复制代码  //判断执行的方法和调用方法是不是同一个方法
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
shell复制代码> 这里给加了@Configuration注解的类使用cglib动态代理产生的代理类:
>1. 主要是使用BeanMethodInterceptor类,在bean被实例化的时候,进行拦截。
>2. 怎么做到在configDao2()方法中调用了configDao1(),而configDao1没有被创建两次?
>> 主要是判断执行的方法和调用方法是不是同一个方法,即在执行configDao2()方法的时候,调用了configDao1()方法。
>> 所以在再次执行configDao1()方法时候,判断出发起调用的主方法是configDao2,即执行方法和调用方法不是同一方法。
>
>3.1 如果是同一个方法:调用代理对象的方法,执行被代理类的方法
>> cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
>3.2 不是同一个方法:调用getBean方法
>> return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
### spring初始化总结
#### spring的扩展点(5个)
>1. BeanPostProcessor
>>插手bean的实例过程、实例化之后,在bean没有被spring的bean容器管理之前干活。
>>经典场景:@PostConstruct、aop
>2. BeanFactoryPostProcessor
>>spring的bean容器当中任意的一个bean被new出来之前执行,针对beanFactory来建设。
>>经典场景:ConfigurationClassPostProcessor#postProcessBeanFactory,针对配置类(@Configuration注解)加上cglib代理。
>3. BeanFactoryRegistryPostProcessor
>>是BeanFactoryPostProcessor的子类,在BeanFactoryPostProcessor之前执行?因为源码当中先遍历BeanFactoryRegistryPostProcessor(有spring提供的,还有自定义)自定义的先执行。
>>经典场景:ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry,扫描、3种import的扫描、@Bean的扫描,判断配置类是否是一个完整的配置类
>4. ImportSelector
>>通过这个方法selectImports返回一个类名(全名)数组,把他变成beanDefinition,动态添加beanDefinition(这个BeanDefinition是写死的,因为这个BeanDefinition是有spring内部生成的,我们无法改变这些BeanDefinition)
>5. ImportBeanDefinitionRegistrar
>>registerBeanDefinitions方法可以得到BeanDefinitionRegistry故而可以动态添加BeanDefinition,还可以改变BeanDefinition。
>>经典场景:mybatis的mapperScan

#### spring bean初始化结构图:
* ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190911160302476.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI0MTAxMzU3,size_16,color_FFFFFF,t_70)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring5源码解析一

发表于 2021-08-06

Spring5源码解析(扫描bean)

测试用例代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
typescript复制代码//AppConfig.java类
@ComponentScan({"com.llsydn"})
@Configuration
public class AppConfig {
@Bean
public ConfigDao1 configDao1(){ //自己写一个configDao1类即可
return new ConfigDao1();
}

@Bean
public ConfigDao2 configDao2(){ //自己写一个configDao2类即可
configDao1();
return new ConfigDao2();
}
}
//IndexDao.java类
@Component
public class IndexDao {
public void query(){
System.out.println("query");
}
}
//Test.java类
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
ac.refresh();

IndexDao indexDao = ac.getBean(IndexDao.class);
System.out.println(indexDao);
indexDao.query();
}
  • 这里我是直接用AnnotationConfigApplicationContext类初始化spring的环境,这个类是基于注解配置应用上下文(即是用注解的方式初始化一个spring容器)
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码Spring中用来注解Bean定义的类有两个:
AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContex。
AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本

两者的用法以及对注解的处理方式几乎没有什么差别
通过分析这个类我们知道注册一个bean到spring容器有两种办法:
一、直接将注解Bean注册到容器中:(参考)public void register(Class<?>... annotatedClasses)
但是直接把一个注解的bean注册到容器当中也分为两种方法
1、在初始化容器时注册并且解析
2、也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理。

二、通过扫描指定的包及其子包下的所有类
扫描其实同上,也是两种方法,初始化的时候扫描,和初始化之后再扫描

下面进入spring容器初始化源码分析

  • (1) AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
scala复制代码public class AnnotationConfigApplicationContext extends GenericApplicationContext
/**
* 这个类顾名思义是一个reader,一个读取器
* 读取什么呢?还是顾名思义AnnotatedBeanDefinition意思是读取一个被加了注解的bean
* 这个类在构造方法中实例化的
*/
private final AnnotatedBeanDefinitionReader reader;

/**
* 同意顾名思义,这是一个扫描器,扫描所有加了注解的bean
* 同样是在构造方法中被实例化的
*/
private final ClassPathBeanDefinitionScanner scanner;

/**
* 初始化一个bean的读取和扫描器
* 何谓读取器和扫描器参考上面的属性注释
* 默认构造函数,如果直接调用这个默认构造方法,需要在稍后通过调用其register()
* 去注册配置类(javaconfig),并调用refresh()方法刷新容器,
* 触发容器对注解Bean的载入、解析和注册过程
*/
public AnnotationConfigApplicationContext() {
/**
* 父类的构造方法
* 创建一个读取注解的Bean定义读取器
* 什么是bean定义?BeanDefinition
*/
this.reader = new AnnotatedBeanDefinitionReader(this);

//可以用来扫描包或者类,继而转换成bd
//但是实际上我们扫描包工作不是scanner这个对象来完成的
//是spring自己new的一个ClassPathBeanDefinitionScanner
//这里的scanner仅仅是为了程序员能够在外部调用AnnotationConfigApplicationContext对象的scan方法
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

这里创建一个AnnotationConfigApplicationContext对象,主要是做了3个主要的操作。
1.创建一个new DefaultListableBeanFactory() 是一个Bean工厂容器
2.创建一个new AnnotatedBeanDefinitionReader(this),是Bean的读取器
3.创建一个new ClassPathBeanDefinitionScanner(this),是Bean的扫描器

为什么这里会创建一个DefaultListableBeanFactory()实例的beanFactory?
1.这个beanFactory主要是用来存放Spring管理的Bean对象,一个Bean存放的工厂。
2.怎么会调用了这步new DefaultListableBeanFactory()?

因为AnnotationConfigApplicationContext继承了GenericApplicationContext,即在创建AnnotationConfigApplicationContext对象,会先执行父类GenericApplicationContext的构造方法。所以这里是在父类的构造方法中,执行了new DefaultListableBeanFactory()创建了一个beanFactory对象。
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}

AnnotationConfigApplicationContext 继承了GenericApplicationContext
GenericApplicationContext 实现了 BeanDefinitionRegistry

  1. 即有:AnnotationConfigApplicationContext 也实现了BeanDefinitionRegistry,AnnotationConfigApplicationContext 也是一个registry类。
  2. 这个registry比较重要,registry有registerBeanDefinition(注册一个bean定义到bean工厂)、getBeanDefinition(从bean工厂获取一个Bean定义)等功能。所以AnnotationConfigApplicationContext 也是有可以往bean工厂中注册bean的能力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
scss复制代码//创建一个bean读取器过程分析:
this.reader = new AnnotatedBeanDefinitionReader(this);

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
this.registry = registry; //将registry赋值
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
//主要方法
registerAnnotationConfigProcessors(registry, null);
}

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
//获取到刚创建完的DefaultListableBeanFactory对象,然后给这个对象的某些属性赋值。
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
//AnnotationAwareOrderComparator主要能解析@Order注解和@Priority
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
//ContextAnnotationAutowireCandidateResolver提供处理延迟加载的功能
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}

//给Spring容器添加Spring内部的特殊Bean对象(7个)
//1.往BeanDefinitionMap注册一个ConfigurationClassPostProcessor
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
//BeanDefinitio的注册,这里很重要,需要理解注册每个bean的类型
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//需要注意的是ConfigurationClassPostProcessor的类型是BeanDefinitionRegistryPostProcessor
//而 BeanDefinitionRegistryPostProcessor 最终实现BeanFactoryPostProcessor这个接口
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

//2.往BeanDefinitionMap注册一个AutowiredAnnotationBeanPostProcessor
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//AutowiredAnnotationBeanPostProcessor 实现了 MergedBeanDefinitionPostProcessor
//MergedBeanDefinitionPostProcessor 最终实现了 BeanPostProcessor
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

//3.往BeanDefinitionMap注册一个RequiredAnnotationBeanPostProcessor
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

//4.往BeanDefinitionMap注册一个CommonAnnotationBeanPostProcessor
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}

//5.往BeanDefinitionMap注册一个PersistenceAnnotationBeanPostProcessor
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}

//6.往BeanDefinitionMap注册一个EventListenerMethodProcessor
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}

//7.往BeanDefinitionMap注册一个DefaultEventListenerFactory
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}

//将**Processor类型的对象,注册到bean工厂中
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//这里主要的代码,将bean定义注册到bean工厂当中
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}

这里创建一个AnnotatedBeanDefinitionReader对象,主要是做了2个主要的操作:
1.给在GenericApplicationContext()刚创建的beanFactory对象的某些属性赋值:

beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);(主要能解析@Order注解和@Priority)
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());(提供处理延迟加载的功能)

2.往bean工厂中注册6个spring内部对象,主要是**BeanPostProcessor类型的对象。(Spring的扩展点之一)

这里特别重要的类是ConfigurationClassPostProcessor,这个类完成bean的扫描。

  • (2)ac.register(AppConfig.class);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
scss复制代码/**
* 注册单个bean给容器
* 比如有新加的类可以用这个方法
* 但是注册之后需要手动调用refresh方法去触发容器解析注解
*
* 有两个意思:
* 他可以注册一个配置类
* 他还可以单独注册一个bean
*/
public void register(Class<?>... annotatedClasses) {
this.reader.register(annotatedClasses);
}

public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}

public void registerBean(Class<?> annotatedClass) {
//真正的注册bean的方法
doRegisterBean(annotatedClass, null, null, null);
}

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
/**
* 根据指定的bean创建一个AnnotatedGenericBeanDefinition
* 这个AnnotatedGenericBeanDefinition可以理解为一个数据结构
* AnnotatedGenericBeanDefinition包含了类的其他信息,比如一些元信息:scope,lazy等等
*/
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
/**
* 判断这个类是否需要跳过解析
* 通过代码可以知道spring判断是否跳过解析,主要判断类有没有加注解
*/
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
//不知道
abd.setInstanceSupplier(instanceSupplier);
/**
* 得到类的作用域
*/
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
/**
* 把类的作用域添加到数据结构结构中
*/
abd.setScope(scopeMetadata.getScopeName());
/**
* 生成类的名字通过beanNameGenerator
*/
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
/**
* 处理类当中的通用注解
* 分析源码可以知道他主要处理
* Lazy DependsOn Primary Role等等注解
* 处理完成之后processCommonDefinitionAnnotations中依然是把他添加到数据结构当中
*/
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

/**
* 如果在向容器注册注解Bean定义时,使用了额外的限定符注解则解析
* 关于Qualifier和Primary前面的课当中讲过,主要涉及到spring的自动装配
* 这里需要注意的
* byName和qualifiers这个变量是Annotation类型的数组,里面存不仅仅是Qualifier注解
* 理论上里面里面存的是一切注解,所以可以看到下面的代码spring去循环了这个数组
* 然后依次判断了注解当中是否包含了Primary,是否包含了Lazyd
*/
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
////如果配置了@Primary注解,如果加了则作为首选
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
//懒加载,前面加过
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
//如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一个根据名字自动装配的限定符
//这里难以理解,后面会详细介绍
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}

/**
* 这个BeanDefinitionHolder也是一个数据结构(beanName,beanDefinition,aliases[])
*/
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

/**
* ScopedProxyMode 这个知识点比较复杂,需要结合web去理解
*/
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

/**
* 把上述的这个数据结构注册给registry
* registy就是AnnotatonConfigApplicationContext
* AnnotatonConfigApplicationContext在初始化的時候通过调用父类的构造方法
* 实例化了一个DefaultListableBeanFactory
* *registerBeanDefinition里面就是把definitionHolder这个数据结构包含的信息注册到
* DefaultListableBeanFactory这个工厂
*/
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

//将beanDefinition注册到bean工厂中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException{
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

//DefaultListableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//主要代码:
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}

ac.register(AppConfig.class);的主要作用是将AppConfig类注册到bean工厂中。
即是到目前为止,bean工厂中已经有了7个beanDefinition。在这里插入图片描述

  • (3)ac.refresh(); 这里最重要的一步,实现bean的扫描和初始化阶段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
scss复制代码public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备工作包括设置启动时间,是否激活标识位,
// 初始化属性源(property source)配置
prepareRefresh();

//返回一个factory 为什么需要返回一个工厂
//因为要对工厂进行初始化
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

//准备工厂
prepareBeanFactory(beanFactory);
try {
//这个方法在当前版本的spring是没用任何代码的
//可能spring期待在后面的版本中去扩展吧
postProcessBeanFactory(beanFactory);

//在spring的环境中去执行已经被注册的 factoryBean processors
//设置执行自定义的ProcessBeanFactory 和spring内部自己定义的 (重要,实现bean的扫描等)
invokeBeanFactoryPostProcessors(beanFactory);

//注册beanPostProcessor
registerBeanPostProcessors(beanFactory);

initMessageSource();

//初始化应用事件广播器
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

//实例化单列的bean对象(重要)
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
throw ex;
}
}
}
  • prepareRefresh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kotlin复制代码protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);

if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}

// Initialize any placeholder property sources in the context environment
//这个方法目前没有子类去实现
//估计spring是期待后面的版本有子类去实现吧
initPropertySources();

// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();

// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
  • obtainFreshBeanFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

//GenericApplicationContext
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
  • prepareBeanFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
scss复制代码/**
* 配置其标准的特征,比如上下文的加载器ClassLoader和post-processors回调
* 此处的beanFactory参数等于DefaultListableFactory
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
//bean表达式解释器,能够获取bean当中的属性在前台页面
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//对象与string类型的转换 <property red="dao">
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

//添加一个后置管理器
//ApplicationContextAwareProcessor 能够在bean中获得到各种*Aware(*Aware都有其作用)
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

//意思就是:使得ApplicationContextAware接口实现类在自动装配时不能被注入applicationContext对象的依赖。
//(不能使用xml的set方法或构造方式注入,但是可以用@Autowired注入)
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

//意思是如果自定义的Bean中没有名为"systemProperties"和"systemEnvironment"的Bean,
// 则注册两个Bena,Key为"systemProperties"和"systemEnvironment",Value为Map,
// 这两个Bean就是一些系统配置和系统环境信息
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
//直接往beanFactory工厂中添加bean对象(该对象已经被new出来的)
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException();
}
addSingleton(beanName, singletonObject);
}
}
//往singletonObjects对象中添加bean实例
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

prepareBeanFactory方法的主要作用:
1.给beanFactory的某些属性赋值。
2.给beanFactory添加BeanPostProcessor:ApplicationContextAwareProcessor(可以插手bean的初始化,扩展点之一)
3.给beanFactory添加系统配置和系统环境信息等实例。

  • invokeBeanFactoryPostProcessors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
scss复制代码protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//这个地方需要注意getBeanFactoryPostProcessors()是获取手动给spring的BeanFactoryPostProcessor
//自定义并不仅仅是程序员自己写的
//自己写的可以加companent也可以不加
//如果加了getBeanFactoryPostProcessors()这个地方得不得,是spring自己扫描的
//为什么得不到getBeanFactoryPostProcessors()这个方法是直接获取一个list,
//这个list是在AnnotationConfigApplicationContext被定义
//所谓的自定义的就是你手动调用AnnotationConfigApplicationContext.addBeanFactoryPostProcesor();
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}

public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

//定义了两个list存放
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

//自定义的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}else {
regularPostProcessors.add(postProcessor);
}
}

//这个currentRegistryProcessors 放的是spring内部自己实现了BeanDefinitionRegistryPostProcessor接口的对象
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

//BeanDefinitionRegistryPostProcessor 等于 BeanFactoryPostProcessor
//getBeanNamesForType 根据bean的类型获取bean的名字ConfigurationClassPostProcessor
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
//这个地方可以得到一个BeanFactoryPostProcessor,因为是spring默认在最开始自己注册的
//为什么要在最开始注册这个呢?
//因为spring的工厂需要许解析去扫描等等功能
//而这些功能都是需要在spring工厂初始化完成之前执行
//要么在工厂最开始的时候、要么在工厂初始化之中,反正不能再之后
//因为如果在之后就没有意义,因为那个时候已经需要使用工厂了
//所以这里spring'在一开始就注册了一个BeanFactoryPostProcessor,用来插手springfactory的实例化过程
//在这个地方断点可以知道这个类叫做ConfigurationClassPostProcessor
//ConfigurationClassPostProcessor那么这个类能干嘛呢?可以参考源码
//下面我们对这个牛逼哄哄的类(他能插手spring工厂的实例化过程还不牛逼吗?)重点解释
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
//排序不重要,况且currentRegistryProcessors这里也只有一个数据
sortPostProcessors(currentRegistryProcessors, beanFactory);
//合并list,不重要(为什么要合并,因为还有自己的)
registryProcessors.addAll(currentRegistryProcessors);

//最重要。注意这里是方法调用
//执行所有BeanDefinitionRegistryPostProcessor(开始执行扫描包)
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

//执行完成了所有BeanDefinitionRegistryPostProcessor
//这个list只是一个临时变量,故而要清除
currentRegistryProcessors.clear();

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}

// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
//执行BeanFactoryPostProcessor的回调,前面不是吗?
//前面执行的BeanFactoryPostProcessor的子类BeanDefinitionRegistryPostProcessor的回调
//这是执行的是BeanFactoryPostProcessor postProcessBeanFactory
//ConfuguratuonClassPpostProcssor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
//自定义BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}

else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}

// 这里为什么要再重复执行一遍?
// 因为经过上面的ConfuguratuonClassPpostProcssor对bean的扫描,扫描到的bean对象有可能是实现了BeanFactoryPostProcessor接口的,所以要这这些扫描处理的bena进行再一步处理

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
//ConfigurationClassPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}

// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}

private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
//因为只有一条数据:ConfigurationClassPostProcessor
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}

//ConfigurationClassPostProcessor
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException();
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException();
}
this.registriesPostProcessed.add(registryId);
//执行
processConfigBeanDefinitions(registry);
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//定义一个list存放app 提供的bd(项目当中提供了@Compent)
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//获取容器中注册的所有bd名字
//7个
String[] candidateNames = registry.getBeanDefinitionNames();

/**
* Full , Lite
*/
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
//果BeanDefinition中的configurationClass属性为full或者lite,则意味着已经处理过了,直接跳过
//这里需要结合下面的代码才能理解
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//判断是否是Configuration类,如果加了Configuration下面的这几个注解就不再判断了
// 还有 add(Component.class.getName());
// candidateIndicators.add(ComponentScan.class.getName());
// candidateIndicators.add(Import.class.getName());
// candidateIndicators.add(ImportResource.class.getName());
//beanDef == appconfig
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//BeanDefinitionHolder 也可以看成一个数据结构
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}

// 排序,根据order,不重要
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});


SingletonBeanRegistry sbr = null;
//如果BeanDefinitionRegistry是SingletonBeanRegistry子类的话,
// 由于我们当前传入的是DefaultListableBeanFactory,是SingletonBeanRegistry 的子类
// 因此会将registry强转为SingletonBeanRegistry
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
//是否有自定义的
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
//SingletonBeanRegistry中有id为 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
//如果有则利用他的,否则则是spring默认的
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}

//实例化ConfigurationClassParser 为了解析各个配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);

//实例化2个set,candidates用于将之前加入的configCandidates进行去重
//因为可能有多个配置类重复了
//alreadyParsed用于判断是否处理过
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//很重要(扫描bean)
parser.parse(candidates);
parser.validate();
//map.keyset
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}

/**
* 这里值得注意的是扫描出来的bean当中可能包含了特殊类
* 比如ImportBeanDefinitionRegistrar那么也在这个方法里面处理
* 但是并不是包含在configClasses当中
* configClasses当中主要包含的是importSelector
* 因为ImportBeanDefinitionRegistrar在扫描出来的时候已经被添加到一个list当中去了
*/
//bd 到 map 除却普通 (将import的bean注册到bean工厂)(重要)
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);

candidates.clear();
//由于我们这里进行了扫描,把扫描出来的BeanDefinition注册给了factory
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
}

//ConfigurationClassParser
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
//根据BeanDefinition 的类型 做不同的处理,一般都会调用ConfigurationClassParser#parse 进行解析
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
//解析注解对象,并且把解析出来的bd放到map,但是这里的bd指的是普通的
//何谓不普通的呢?比如@Bean 和各种beanFactoryPostProcessor得到的bean不在这里put
//但是是这里解析,只是不put而已
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
//处理延迟加载的importSelect?为什么要延迟加载,估计就是为了延迟吧
processDeferredImportSelectors();
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 处理Imported 的情况
// 就是当前这个注解类有没有被别的类import
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an imports.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
//具体的实现(重要)
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//一个map,用来存放扫描出来的bean(注意这里的bean不是对象,仅仅bean的信息,因为还没到实例化这一步)
this.configurationClasses.put(configClass, configClass);
}

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// 处理内部类
processMemberClasses(configClass, sourceClass);

// 处理@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
}

// 处理@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
//扫描普通类=componentScan=com.llsydn
//这里扫描出来所有@Component
//并且把扫描的出来的普通bean放到map当中
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

//检查扫描出来的类当中是否还有configuration
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//检查到有的,再执行一次parse方法
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}

/**
* 上面的代码就是扫描普通类----@Component
* 并且放到了map当中
*/
// Process any @Import annotations
//处理@Import imports 3种情况
//ImportSelector
//普通类
//ImportBeanDefinitionRegistrar
//这里和内部地柜调用时候的情况不同
/**
* 这里处理的import是需要判断我们的类当中时候有@Import注解
* 如果有这把@Import当中的值拿出来,是一个类
* 比如@Import(xxxxx.class),那么这里便把xxxxx传进去进行解析
* 在解析的过程中如果发觉是一个importSelector那么就回调selector的方法
* 返回一个字符串(类名),通过这个字符串得到一个类
* 继而在递归调用本方法来处理这个类
*
* 判断一组类是不是imports(3种import)
*/
processImports(configClass, sourceClass, getImports(sourceClass), true);

// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}

// 处理@Bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

// Process default methods on interfaces
processInterfaces(configClass, sourceClass);

// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}

// No superclass -> processing is complete
return null;
}

//ComponentScanAnnotationParser,扫描普通的@component
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//扫描包(重点)
return scanner.doScan(StringUtils.toStringArray(basePackages));
}

//ClassPathBeanDefinitionScanner
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
//扫描basePackage路径下的java文件
//符合条件的并把它转成BeanDefinition类型
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//解析scope属性
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
//如果这个类是AbstractBeanDefinition的子类
//则为他设置默认值,比如lazy,init destory
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
//检查并且处理常用的注解
//这里的处理主要是指把常用注解的值设置到AnnotatedBeanDefinition当中
//当前前提是这个类必须是AnnotatedBeanDefinition类型的,说白了就是加了注解的类
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将bean定义注册到bean工厂中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

//扫描包,将class转成beanDefinition
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}

//利用asm技术读取class文件,并将class文件转成beanDefinition
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//asm 读取class文件
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
}
}
}
}
return candidates;
}

invokeBeanFactoryPostProcessors的主要作用:
1.执行spring内部的**BeanFactoryPostProcessor的方法。(ConfigurationClassPostProcessor)

ConfigurationClassPostProcessor主要的作用就是实现bean的扫描,并实现将beanDefinition注册到bean工厂中

BeanFactoryPostProcessor
spring的扩展点之一:
1.实现该接口,可以在spring的bean创建之前修改bean的定义属性。
2.spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,
3.并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。
4.可以同时配置多个BeanFactoryPostProcessor,并通过设置’order’属性来控制各个BeanFactoryPostProcessor的执行次序。
5.BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。

BeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor 。是对BeanFactoryPostProcessor 的扩展,新增了postProcessBeanDefinitionRegistry方法,可以往Bean工厂中,注册一个BeanDefinition对象。

1
复制代码

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring Cloud Alibaba微服务学习一:服务发

发表于 2021-08-06

“这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

为什么出现Spring Cloud Alibaba?

Spring Cloud的官网中:

在以往的Spring Cloud中官方也是推荐使用Netflix的组件,但是随着Netflix的多个组件进入维护状态,可以说Netflix的组件已经不能使用了,因为可能出现潜在风险。对于微服务来说,最重要的几个组件:服务发现、断路器、网关、负载均衡。然而这些组件都进入了维护状态。所以随之Ailibaba的组件就成为了我们的首选。

Spring Cloud Alibaba的主要组件

  • Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等都各维度保护服务的稳定性。
  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • RabbitMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时、高可靠的消息发布与订阅服务。
  • Dubbo:高性能的Java RPC框架。
  • Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
  • Alibaba Cloud ACM:一款在分布式结构环境中对应用配置进行集中式管理和推送的应用配置中心产品。
  • Alibaba Cloud SchedulerX:分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时任务调度服务。
  • Alibaba Cloud SMS:覆盖全球的短信服务,友好、高效、只能的互联网通讯能力
  • Alibaba Cloud OSS:对象存储服务。

像OSS SMS、SchedulerX、ACM这些是阿里云的收费产品,这些也不是构建微服务的必需品,我们可以不用。

什么是Nacos?

服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理:

Kubernetes Service

gRPC & Dubbo RPC Service

Spring Cloud RESTful Service

使用Nacos Server并注册应用

1.下载Nacos。下载地址:github.com/alibaba/nac…

2.解压Nacos的压缩包。

3.启动Nacos。

windows下启动

cmd startup.cmd或者双击startup.cmd

linux/Unix/Mac启动

sh startup.sh -m standalone

4.启动完成

nacos有一个完整的web界面,在上面有很多完善的功能。在浏览器打开http://localhost:8848/nacos。默认的用户名为nacos,密码为nacos

nacos的web界面

5.编写应用,注册到Nacos中

创建项目:

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
xml复制代码<?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.msr</groupId>
<artifactId>cloud-v2</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>cloud-user-center-nacos-7003</module>
<module>cloud-user-center-nacos-7005</module>
</modules>
<packaging>pom</packaging>

<!-- 统一管理jar包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>8.0.18</mysql.version>
<druid.version>1.1.16</druid.version>
<druid.spring.boot.starter.version>1.1.10</druid.spring.boot.starter.version>
<spring.boot.version>2.2.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<mybatis-spring-boot-starter.version>2.1.1</mybatis-spring-boot-starter.version>
<hutool-all.version>5.1.0</hutool-all.version>
</properties>

<!-- 子模块继承之后,提供作用:锁定版本 + 子module不用谢groupId和version -->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>


</project>

创建模块(cloud-user-center-nacos-7003)

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
xml复制代码<?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">
<parent>
<artifactId>cloud-v2</artifactId>
<groupId>com.msr</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>cloud-user-center-nacos-7003</artifactId>

<dependencies>
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

**启动类:**UserCenter7003Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码package com.msr.cloudv2.nacos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class UserCenter7003Application {

public static void main(String[] args) {
SpringApplication.run(UserCenter7003Application.class, args);
}
}

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
yaml复制代码server:
port: 7003
spring:
application:
name: cloud-user-center-provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: '*'

创建模块(cloud-user-center-nacos-7005)

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
xml复制代码<?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">
<parent>
<artifactId>cloud-v2</artifactId>
<groupId>com.msr</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>cloud-user-center-nacos-7005</artifactId>

<dependencies>
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码package com.msr.cloudv2.nacos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class Nacos7005Application {

public static void main(String[] args) {
SpringApplication.run(Nacos7005Application.class, args);
}
}

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yaml复制代码server:
port: 7003
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8
application:
name: cloud-user-center-provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: '*'

运行启动类启动,可以看到程序成功注册到Nacos注册中心。

登录Nacos的Web界面查看:已经有两个实列数成功注册进去。

服务之间的相互感知

很明显user-center两个实列已经注册成功,对于一个注册中心来说,里面的服务时可以相互感知的。代码如下:

在7003端口的服务中创建一个UserCenterController,然后两个服务都启动。在浏览器访问http://localhost:7003/nacos/getServiceInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
java复制代码package com.msr.cloudv2.nacos.conttoller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("nacos")
public class UserCenterController {

@Autowired
private DiscoveryClient discoveryClient;

@Autowired
private RestTemplate restTemplate;

@GetMapping("getServiceInfo")
public Object testNacos() {
List<String> services = discoveryClient.getServices();
List<Object> list = new ArrayList<>();
services.stream().distinct().forEach(e -> list.add(discoveryClient.getInstances(e)));
return list;
}

@GetMapping("toCall7005")
public Object call() {
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-user-center-provider-service");
List<ServiceInstance> serviceInstances = instances.stream().filter(e -> e.getPort() == 7005).collect(Collectors.toList());
ServiceInstance serviceInstance = serviceInstances.get(0);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri.toString()+"nacos/test", String.class);
}

@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(5000);
requestFactory.setConnectTimeout(5000);
return new RestTemplate();
}
}

显然,可以通过DiscoveryClient来获取注册在Nacos上的服务信息。接下来可以使用RestTemplate来调用7005端口的服务。

首先在7005端口的服务也创建一个UserCenterController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码package com.msr.cloudv2.nacos.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("nacos")
public class UserCenterController {


@GetMapping("test")
public String testNacos() {
return "welcome to use nacos port :"+7005;
}
}

在浏览器访问http://localhost:7003/nacos/toCall7005

综上所述,注册中Nacos中的服务时可以相互感知,并可以通过一些Http的客户端进行相互之间的调用。

Nacos服务发现的领域模型

  • NameSpace:命名空间,Nacso默认的NameSpace是public。比如在开发中,可以创建一个开发环境和测试环境的NameSpace。这样可以通过指定服务的NameSpace做到环境隔离。
  • Group:分组。在Nacos用作配置中心时使用。
  • **Service:**微服务
  • Cluster:对指定微服务的一个虚拟划分,默认是DEFAULT。
  • **Instance:**微服务实例。
  • Metadata: 只要用于版本控制。比如,我们在开发中可能是多个版本共存的。

配置使用

在Nacos下创建一个名为dev的NameSpace,然后再该NameSpace下创建一个

cloud-user-center-nacos-7003

只需要在配置文件中配置namespace和cluster-name这两项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
yaml复制代码server:
port: 7003
spring:
application:
name: cloud-user-center-provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 开发NameSpace,值为UUID
namespace:
# 列如:广州集群
cluster-name: GZ
management:
endpoints:
web:
exposure:
include: '*'

cloud-user-center-nacos-7005

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
yaml复制代码server:
port: 7005
spring:
application:
name: cloud-user-center-provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 开发NameSpace,值为UUID
namespace: fa47d71f-9bc5-45ae-96c1-a6b9f7c55700
# 北京集群
cluster-name: BJ
management:
endpoints:
web:
exposure:
include: '*'

运行结果

注册中心:服务列表

点击”详情“,可以看到GZ和BJ两个集群

Nacos的元信息

Nacos数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。

设置方式:1.在Nacos的web界面的控制台设置。2.在配置文件设置

在配置文件设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
yaml复制代码server:
port: 7003
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8
application:
name: cloud-user-center-provider-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 开发NameSpace,值为UUID
namespace: fa47d71f-9bc5-45ae-96c1-a6b9f7c55700
# 广州集群
cluster-name: GZ
metadata:
version: v1
info: user-center
management:
endpoints:
web:
exposure:
include: '*'

结语

不积跬步无以至千里,不积小流无以成江海

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…575576577…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%