Merge branch 'ups'

pull/70/head
huifer 4 years ago
commit 68d87e0bdf

@ -0,0 +1,17 @@
name: Merge Branch
on:
push:
branches: [ imgbot ]
jobs:
merge-branch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: everlytic/branch-merge@1.1.0
with:
github_token: ${{ github.token }}
source_ref: ${{ github.ref }}
target_branch: 'master'
commit_message_template: '[Automated] Merged {source_ref} into {target_branch}'

@ -171,6 +171,8 @@
### Netty 技术细节源码分析
* [FastThreadLocal源码分析](docs/Netty/Netty技术细节源码分析/FastThreadLocal源码分析.md)
* [Recycler对象池原理分析](docs/Netty/Netty技术细节源码分析/Recycler对象池原理分析.md)
* [MpscLinkedQueue队列原理分析](docs/Netty/Netty技术细节源码分析/MpscLinkedQueue队列原理分析.md)
* [HashedWheelTimer时间轮原理分析](docs/Netty/Netty技术细节源码分析/HashedWheelTimer时间轮原理分析.md)
## Dubbo
@ -201,7 +203,8 @@
* [RPC模块简析](docs/Dubbo/RPC/RPC模块简析.md)
* [Protocol组件](docs/Dubbo/RPC/Protocol组件.md)
* [Proxy组件](docs/Dubbo/RPC/Proxy组件.md)
* [多协议支持](docs/Dubbo/RPC/多协议支持.md)
* [Dubbo协议](docs/Dubbo/RPC/Dubbo协议.md)
* [Hessian协议](docs/Dubbo/RPC/Hessian协议.md)
### 集群

@ -0,0 +1 @@
努力编写中...

@ -0,0 +1,141 @@
该文所涉及的 netty 源码版本为 4.1.6。
## Netty 时间轮 HashedWheelTimer 是什么
Netty 的时间轮 HashedWheelTimer 给出了一个粗略的定时器实现,之所以称之为粗略的实现是因为该时间轮并没有严格的准时执行定时任务,而是在每隔一个时间间隔之后的时间节点执行,并执行当前时间节点之前到期的定时任务。当然具体的定时任务的时间执行精度可以通过调节 HashedWheelTimer 构造方法的时间间隔的大小来进行调节,在大多数网络应用的情况下,由于 IO 延迟的存在,并不会严格要求具体的时间执行精度,所以默认的 100ms 时间间隔可以满足大多数的情况,不需要再花精力去调节该时间精度。
## HashedWheelTimer 的实现原理
### HashedWheelTimer 内部的数据结构
```java
private final HashedWheelBucket[] wheel;
```
HashedWheelTimer 的主体数据结构 wheel 是一个由多个链表所组成的数组,默认情况下该数组的大小为 512。当定时任务准备加入到时间轮中的时候将会以其等待执行的时间为依据选择该数组上的一个具体槽位上的链表加入。
```java
private HashedWheelTimeout head;
private HashedWheelTimeout tail;
```
在这个 wheel 数组中,每一个槽位都是一条由 HashedWheelTimeout 所组成的链表,其中链表中的每一个节点都是一个等待执行的定时任务。
### HashedWheelTimer 内部的线程模型
在 HashedWheelTimer 中,其内部是一个单线程的 worker 线程,通过类似 eventloop 的工作模式进行定时任务的调度。
```java
@Override
public void run() {
// Initialize the startTime.
startTime = System.nanoTime();
if (startTime == 0) {
// We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.
startTime = 1;
}
// Notify the other threads waiting for the initialization at start().
startTimeInitialized.countDown();
do {
final long deadline = waitForNextTick();
if (deadline > 0) {
transferTimeoutsToBuckets();
HashedWheelBucket bucket =
wheel[(int) (tick & mask)];
bucket.expireTimeouts(deadline);
tick++;
}
} while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
// Fill the unprocessedTimeouts so we can return them from stop() method.
for (HashedWheelBucket bucket: wheel) {
bucket.clearTimeouts(unprocessedTimeouts);
}
for (;;) {
HashedWheelTimeout timeout = timeouts.poll();
if (timeout == null) {
break;
}
unprocessedTimeouts.add(timeout);
}
}
```
简单看到 HashedWheelTimer 内部的 woker 线程的 run()方法,在其首先会记录启动时间作为 startTime 作为接下来调度定时任务的时间依据,而之后会通过 CountDownLatch 来通知所有外部线程当前 worker 工作线程已经初始化完毕。之后的循环体便是当时间轮持续生效的时间里的具体调度逻辑。时间刻度是时间轮的一个重要属性,其默认为 100ms此处的循环间隔便是时间轮的时间刻度默认情况下就是间隔 100ms 进行一次调度循环。工作线程会维护当前工作线程具体循环了多少轮,用于定位具体执行触发时间轮数组上的哪一个位置上的链表。当时间轮准备 shutdown 的阶段,最后的代码会对未执行的任务整理到未执行的队列中。
由此可见worker 线程的 run()方法中基本定义了工作线程的整个生命周期,从初始的初始化到循环体中的具体调度,最后到未执行任务的具体清理。整体的调度逻辑便主要在这里执行。值得注意的是,在这里的前提下,每个 HashedWheelTimer 时间轮都会有一个工作线程进行调度,所以不需要在 netty 中在每一个连接中单独使用一个 HashedWheelTimer 来进行定时任务的调度,否则可能将对性能产生影响。
### 向 HashedWheelTimer 加入一个定时任务的流程
当调用 HashedWheelTimer 的 newTimeout()方法的时候,即是将定时任务加入时间轮中的 api。
```java
@Override
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
if (task == null) {
throw new NullPointerException("task");
}
if (unit == null) {
throw new NullPointerException("unit");
}
start();
long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
timeouts.add(timeout);
return timeout;
}
```
当此次是首次向该时间轮加入定时任务的时候,将会通过 start()方法开始执行上文所述的 worker 工作线程的启动与循环调度逻辑,这里暂且不提。之后计算定时任务触发时间相对于时间轮初始化时间的相对时间间隔 deadline并将其包装为一个链表节点 HashedWheelTimeout ,投入到 timeouts 队列中,等待 worker 工作线程在下一轮调度循环中将其加入到时间轮的具体链表中等待触发执行timeouts 的实现是一个 mpsc 队列,关于 mpsc 队列可以查看[此文](https://github.com/doocs/source-code-hunter/blob/master/docs/Netty/Netty%E6%8A%80%E6%9C%AF%E7%BB%86%E8%8A%82%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/MpscLinkedQueue%E9%98%9F%E5%88%97%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md),这里也符合多生产者单消费者的队列模型。
### HashedWheelTimer 中工作线程的具体调度
```java
do {
final long deadline = waitForNextTick();
if (deadline > 0) {
transferTimeoutsToBuckets();
HashedWheelBucket bucket =
wheel[(int) (tick & mask)];
bucket.expireTimeouts(deadline);
tick++;
}
} while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
```
在 HashedWheelTimer 中的工作线程 run()方法的主要循环中,主要分为三个步骤。
首先 worker 线程会通过 waitForNextTick()方法根据时间轮的时间刻度等待一轮循环的开始,在默认情况下时间轮的时间刻度是 100ms那么此处 worker 线程也将在这个方法中 sleep 相应的时间等待下一轮循环的开始。此处也决定了时间轮的定时任务时间精度。
当 worker 线程经过相应时间间隔的 sleep 之后,也代表新的一轮调度开始。此时,会通过 transferTimeoutsToBuckets()方法将之前刚刚加入到 timeouts 队列中的定时任务放入到时间轮具体槽位上的链表中。
```java
for (int i = 0; i < 100000; i++) {
HashedWheelTimeout timeout = timeouts.poll();
if (timeout == null) {
// all processed
break;
}
if (timeout.state() == HashedWheelTimeout.ST_CANCELLED
|| !timeout.compareAndSetState(HashedWheelTimeout.ST_INIT, HashedWheelTimeout.ST_IN_BUCKET)) {
timeout.remove();
continue;
}
long calculated = timeout.deadline / tickDuration;
long remainingRounds = (calculated - tick) / wheel.length;
timeout.remainingRounds = remainingRounds;
final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past.
int stopIndex = (int) (ticks & mask);
HashedWheelBucket bucket = wheel[stopIndex];
bucket.addTimeout(timeout);
}
```
首先,在每一轮的调度中,最多只会从 timeouts 队列中定位到时间轮 100000 个定时任务,这也是为了防止在这里耗时过久导致后面触发定时任务的延迟。在这里会不断从 timeouts 队列中获取刚加入的定时任务。具体的计算流程便是将定时任务相对于时间轮初始化时间的相对间隔与时间轮的时间刻度相除得到相对于初始化时间的具体轮数,之后便在减去当前轮数得到还需要遍历几遍整个时间轮数组得到 remainingRounds最后将轮数与时间轮数组长度-1 相与,得到该定时任务到底应该存放到时间轮上哪个位置的链表。用具体的数组举个例子,该时间轮初始化时间为 12 点,时间刻度为 1 小时,时间轮数组长度为 8当前时间 13 点,当向时间轮加入一个明天 13 点执行的任务的时候,首先得到该任务相对于初始化的时间间隔是 25 小时,也就是需要 25 轮调度,而当前 13 点,当前调度轮数为 1因此还需要 24 轮调度,就需要再遍历 3 轮时间轮,因此 remainingRounds 为 3再根据 25 与 8-1 相与的结果为 1因此将该定时任务放置到时间轮数组下标为 1 的链表上等待被触发。这便是一次完整的定时任务加入到时间轮具体位置的计算。
在 worker 线程的最后,就需要来具体执行定时任务了,首先通过当前循环轮数与时间轮数组长度-1 相与的结果定位具体触发时间轮数组上哪个位置上的链表,再通过 expireTimeouts()方法依次对链表上的定时任务进行触发执行。这里的流程就相对很简单,链表上的节点如果 remainingRounds 小于等于 0那么就可以直接执行这个定时任务如果 remainingRounds 大于 0那么显然还没有到达触发的时间点则将其-1 等待下一轮的调度之后再进行执行。在继续回到上面的例子,当 14 点来临之时,此时工作线程将进行第 2 轮的调度,将会把 2 与 8-1 进行相与得到结果 2那么当前工作线程就会选择时间轮数组下标为 2 的链表依次判断是否需要触发,如果 remainingRounds 为 0 将会直接触发,否则将会将 remainingRounds-1 等待下一轮的执行。

@ -0,0 +1,69 @@
该文所涉及的 netty 源码版本为 4.1.6。
## MpscLinkedQueue 是什么
在 Netty 的核心中的核心成员 NioEventLoop 中,其中任务队列的实现 taskQueue 便是 MpscLinkedQueue。MpscLinkedQueue 是 Netty 所实现的一个基于多生产者单消费者的无锁队列,针对 NioEventLoop 中任务队列的特点,其单消费者的场景在一开始就避免了从队列中取数据时加锁的必要,而其最精妙的地方便是在多生产者并发从队列中添加数据的时候也没有加锁,达到 Netty 所期望的高性能实现。这是如何实现的?
## MpscLinkedQueue 无锁并发线程安全写入原理
### MpscLinkedQueue 对于尾结点的维护
首先MpscLinkedQueue 继承自 AtomicReference也就是说 MpscLinkedQueue 通过继承自 AtomicReference 的方式,显式地维护了一个提供原子读写能力的变量 value。而在 MpscLinkedQueue 中,这个 value 是其内部维护的队列的尾结点。
### MpscLinkedQueue 对于头结点的维护
而后,来看 MpscLinkedQueue 的构造方法。
```java
MpscLinkedQueue() {
MpscLinkedQueueNode<E> tombstone = new DefaultNode<E>(null);
headRef = new FullyPaddedReference<MpscLinkedQueueNode<E>>();
headRef.set(tombstone);
setTail(tombstone);
}
```
在 MpscLinkedQueue 中,维护着 headRef 头结点字段,其队列内部节点的实现是一个 MpscLinkedQueueNode。MpscLinkedQueueNode 是一个除了存放具体队列元素外只有 next 字段的节点也就是说MpscLinkedQueue 的队列是单向的。在构造方法的最后,通过 setTail()方法的,将 MpscLinkedQueue 的尾结点字段 value 也设置为头结点。MpscLinkedQueue 的头结点字段 headRef 的存在可以方便后续直接从头结点开始的队列操作,消费者可以简单判断头尾节点是否相等来确认队列中是否有元素可以消费。
### MpscLinkedQueue 如何做到线程安全的无锁加入
```java
@Override
@SuppressWarnings("unchecked")
public boolean offer(E value) {
if (value == null) {
throw new NullPointerException("value");
}
final MpscLinkedQueueNode<E> newTail;
if (value instanceof MpscLinkedQueueNode) {
newTail = (MpscLinkedQueueNode<E>) value;
newTail.setNext(null);
} else {
newTail = new DefaultNode<E>(value);
}
MpscLinkedQueueNode<E> oldTail = replaceTail(newTail);
oldTail.setNext(newTail);
return true;
}
private MpscLinkedQueueNode<E> replaceTail(MpscLinkedQueueNode<E> node) {
return getAndSet(node);
}
```
MpscLinkedQueue 的 offer()方法很简短,但是恰恰就是整个添加队列元素加入的流程,当元素被加入的时候,首先判断加入的元素是否是 MpscLinkedQueueNode如果不是则进行封装。之后便是整个操作的重点
- 通过 replaceTail()方法,将当前被加入的节点通过 AtomicReference 所提供的 getAndSet()方法将其设为队列的尾结点,并返回先前的尾结点。这次操作由 UNSAFE 的 CAS 来保证操作的原子性。
- 之后将之前的尾结点的 next 指向新加入的节点,本次加入宣告结束。
整个操作就到此结束这里可以看出MpscLinkedQueue 利用了 AtomicReference 底层 UNSAFE 的能力,通过 CAS 确保新设置进入 value 的节点必定能够和原先的节点达成一个且唯一的联系,那么只需要自顶向下不断通过将这个联系变成引用,那么一条队列便形成了。由于其实现是链表而不是数组,也就没有涉及到资源的竞争,在不加锁的前提下其队列顺序可能不会严格按照加入顺序,但这在当前场景下并不是问题。在这个前提,高并发的插入场景下,每个新进入的新节点都将获取原尾位置 value 上的节点而自身将会被设置为其后驱节点重新放到尾结点位置上CAS 在不加锁的前提下保证了前后节点对应关系的唯一性,完成了并发条件下不加锁的线程安全写入。
### MpscLinkedQueue 不支持 remove()
在 MpscLinkedQueue 中,是不支持 remove()的方法去从队列中移除任意一个元素的。原因很简单,消费者和生产者是无锁的,消费者可以通过比较队首和队尾元素是否一致来保证线程安全地从队首取数据,但是 remove()从队列中任意位置修改数据是线程不安全的,主要体现在移除队尾元素可能会导致正在加入的新元素被丢弃。
## MpscLinkedQueue 另外的实现细节
- MpscLinkedQueue 中的头节点被通过 FullyPaddedReference 封装。其内部前后分别填充 56 字节和 64 字节来进行填充以避免伪共享导致的性能损耗,使得其头结点可以高效被访问。关于伪共享的相关知识可以通过搜索引擎进行查询。
- MpscLinkedQueue 在消费者消费数据后,当将下一个节点设置为头结点的时候,并不是直接进行赋值,而是通过 UNSAFE 来根据偏移量赋值,这样做将略微提高性能,主要是内存屏障 storestrore 和 loadstrore 之间的性能差异。

@ -97,7 +97,7 @@ public enum SearchStrategy {
- 类图
![image-20200824085726621](//images/SpringBoot//SpringBoot/image-20200824085726621.png)
![image-20200824085726621](../../images/SpringBoot//SpringBoot/image-20200824085726621.png)
在看这部分源码之前需要先了解 `Conditional`和`Condition`的源码
@ -421,7 +421,7 @@ for (String type : spec.getTypes()) {
- 在忽略 bean 找到之后做一个类型移除的操作.
![image-20200825140750035](/images/SpringBoot//image-20200825140750035.png)
![image-20200825140750035](../../images/SpringBoot//image-20200825140750035.png)
### 返回值
@ -469,7 +469,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
return ConditionOutcome.match(matchMessage);
```
![image-20200825141506531](/images/SpringBoot//image-20200825141506531.png)
![image-20200825141506531](../../images/SpringBoot//image-20200825141506531.png)
- 到此结果封装完毕.回到方法`org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)` 继续进行
- 再往后就继续执行 spring 的 bean 初始化咯
@ -492,7 +492,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
- 根据类的注解信息我们可以找到有`ResourceBundleCondition`
![image-20200825092343271](/images/SpringBoot//image-20200825092343271.png)
![image-20200825092343271](../../images/SpringBoot//image-20200825092343271.png)
- 获取类名或者方法名的结果是`MessageSourceAutoConfiguration`全路径
@ -592,8 +592,8 @@ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
- 此时我们可以和前文的源码分析连接起来有一个完整的认识了
![image-20200825142332485](/images/SpringBoot//image-20200825142332485.png)
![image-20200825142332485](../../images/SpringBoot//image-20200825142332485.png)
- 最后来看整体类图
![image-20200825142418115](/images/SpringBoot//image-20200825142418115.png)
![image-20200825142418115](../../images/SpringBoot//image-20200825142418115.png)

@ -33,7 +33,7 @@ public @interface ConfigurationPropertiesScan {}
## ConfigurationPropertiesScanRegistrar
![image-20200323094446756](../../../images/SpringBoot/image-20200323094446756.png)
![image-20200323094446756](../../images/SpringBoot/image-20200323094446756.png)
- debug 没有抓到后续补充
@ -137,11 +137,11 @@ public @interface EnableConfigurationProperties {
- 先看输入参数 **metadata**
![image-20200323134135926](../../../images/SpringBoot/image-20200323134135926.png)
![image-20200323134135926](../../images/SpringBoot/image-20200323134135926.png)
- getTypes 结果
![image-20200323134325955](../../../images/SpringBoot/image-20200323134325955.png)
![image-20200323134325955](../../images/SpringBoot/image-20200323134325955.png)
- 源码开始,先找出刚才的对象`org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration`
@ -192,7 +192,7 @@ public @interface EnableConfigurationProperties {
## ConfigurationPropertiesBindingPostProcessor
![image-20200323095626953](../../../images/SpringBoot/image-20200323095626953.png)
![image-20200323095626953](../../images/SpringBoot/image-20200323095626953.png)
### postProcessBeforeInitialization
@ -301,15 +301,15 @@ public @interface EnableConfigurationProperties {
- `annotation`
![image-20200323104711545](../../../images/SpringBoot/image-20200323104711545.png)
![image-20200323104711545](../../images/SpringBoot/image-20200323104711545.png)
- `bindType`
![image-20200323104815305](../../../images/SpringBoot/image-20200323104815305.png)
![image-20200323104815305](../../images/SpringBoot/image-20200323104815305.png)
- 返回对象
![image-20200323105053757](../../../images/SpringBoot/image-20200323105053757.png)
![image-20200323105053757](../../images/SpringBoot/image-20200323105053757.png)
- 此时数据还没有进去
@ -319,7 +319,7 @@ public @interface EnableConfigurationProperties {
直接看结果
![image-20200323105155998](../../../images/SpringBoot/image-20200323105155998.png)
![image-20200323105155998](../../images/SpringBoot/image-20200323105155998.png)
- 上述配置和我在配置文件中写的配置一致
@ -362,7 +362,7 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
}
```
![image-20200323105830138](../../../images/SpringBoot/image-20200323105830138.png)
![image-20200323105830138](../../images/SpringBoot/image-20200323105830138.png)
##### findProperty
@ -428,11 +428,11 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
```
![image-20200323115408877](../../../images/SpringBoot/image-20200323115408877.png)
![image-20200323115408877](../../images/SpringBoot/image-20200323115408877.png)
![image-20200323115701118](../../../images/SpringBoot/image-20200323115701118.png)
![image-20200323115701118](../../images/SpringBoot/image-20200323115701118.png)
![image-20200323115711826](../../../images/SpringBoot/image-20200323115711826.png)
![image-20200323115711826](../../images/SpringBoot/image-20200323115711826.png)
##### getBindHandler
@ -465,7 +465,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
- 最终获取得到的处理器
![image-20200323110603959](../../../images/SpringBoot/image-20200323110603959.png)
![image-20200323110603959](../../images/SpringBoot/image-20200323110603959.png)
- 最后的 bind
@ -499,7 +499,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
```
![image-20200323112945449](../../../images/SpringBoot/image-20200323112945449.png)
![image-20200323112945449](../../images/SpringBoot/image-20200323112945449.png)
配置信息到此绑定成功,关于如何处理集合相关的配置请各位读者自行学习

@ -19,7 +19,7 @@ public enum LogLevel {
- `org.springframework.boot.logging.java.JavaLoggingSystem`
![image-20200323144523848](../../../images/SpringBoot/image-20200323144523848.png)
![image-20200323144523848](../../images/SpringBoot/image-20200323144523848.png)
```JAVA
static {
@ -128,7 +128,7 @@ public static LoggingSystem get(ClassLoader classLoader) {
```
![image-20200323151409473](../../../images/SpringBoot/image-20200323151409473.png)
![image-20200323151409473](../../images/SpringBoot/image-20200323151409473.png)
- 默认日志: `org.springframework.boot.logging.logback.LogbackLoggingSystem`
@ -136,7 +136,7 @@ public static LoggingSystem get(ClassLoader classLoader) {
- 初始化之前
![image-20200323154205484](../../../images/SpringBoot/image-20200323154205484.png)
![image-20200323154205484](../../images/SpringBoot/image-20200323154205484.png)
- 链路
1. `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent`
@ -352,9 +352,9 @@ public static LoggingSystem get(ClassLoader classLoader) {
- 添加配置文件
![image-20200323161442058](../../../images/SpringBoot/image-20200323161442058.png)
![image-20200323161442058](../../images/SpringBoot/image-20200323161442058.png)
![image-20200323161522570](../../../images/SpringBoot/image-20200323161522570.png)
![image-20200323161522570](../../images/SpringBoot/image-20200323161522570.png)
- 此时配置文件地址出现了

@ -9,17 +9,17 @@
2. 全局搜索 yml
![image-20200319083048849](../../../images/SpringBoot/image-20200319083048849.png)
![image-20200319083048849](../../images/SpringBoot/image-20200319083048849.png)
3. 换成`properties`搜索
![image-20200319083140225](../../../images/SpringBoot/image-20200319083140225.png)
![image-20200319083140225](../../images/SpringBoot/image-20200319083140225.png)
4. 我们以`yml`为例打上断点开始源码追踪
看到调用堆栈
![image-20200319083345067](../../../images/SpringBoot/image-20200319083345067.png)
![image-20200319083345067](../../images/SpringBoot/image-20200319083345067.png)
- 一步一步回上去看如何调用具体方法的
@ -29,9 +29,9 @@
### 调用过程
![image-20200319082131146](../../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082131146](../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082544653](../../../images/SpringBoot/image-20200319082544653.png)
![image-20200319082544653](../../images/SpringBoot/image-20200319082544653.png)
`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources`
@ -68,13 +68,13 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader`
![image-20200319084141748](../../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084141748](../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084151997](../../../images/SpringBoot/image-20200319084151997.png)
![image-20200319084151997](../../images/SpringBoot/image-20200319084151997.png)
观察发现里面有一个`YamlPropertySourceLoader`和我们之前找 yml 字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
![image-20200319084357652](../../../images/SpringBoot/image-20200319084357652.png)
![image-20200319084357652](../../images/SpringBoot/image-20200319084357652.png)
初始化完成,后续进行解析了
@ -110,7 +110,7 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
### initializeProfiles
- 初始化`private Deque<Profile> profiles;` 属性
- ![image-20200319084902957](../../../images/SpringBoot/image-20200319084902957.png)
- ![image-20200319084902957](../../images/SpringBoot/image-20200319084902957.png)
### load
@ -135,7 +135,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
- 资源路径可能性
![image-20200319085446640](../../../images/SpringBoot/image-20200319085446640.png)
![image-20200319085446640](../../images/SpringBoot/image-20200319085446640.png)
该方法采用循环每个路径下面都去尝试一遍
@ -190,7 +190,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
```
![image-20200319090446231](../../../images/SpringBoot/image-20200319090446231.png)
![image-20200319090446231](../../images/SpringBoot/image-20200319090446231.png)
- `PropertiesPropertySourceLoader`解析同理不在次展开描述了

@ -53,7 +53,7 @@ public @interface EnableAutoConfiguration {
- 类图
![image-20200320150642022](../../../images/SpringBoot/image-20200320150642022.png)
![image-20200320150642022](../../images/SpringBoot/image-20200320150642022.png)
## getAutoConfigurationMetadata()
@ -107,7 +107,7 @@ public @interface EnableAutoConfiguration {
```
![image-20200320160423991](../../../images/SpringBoot/image-20200320160423991.png)
![image-20200320160423991](../../images/SpringBoot/image-20200320160423991.png)
- `protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";`
@ -131,11 +131,11 @@ public @interface EnableAutoConfiguration {
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
```
![image-20200320162835665](../../../images/SpringBoot/image-20200320162835665.png)
![image-20200320162835665](../../images/SpringBoot/image-20200320162835665.png)
同样找一下 redis
![image-20200320163001728](../../../images/SpringBoot/image-20200320163001728.png)
![image-20200320163001728](../../images/SpringBoot/image-20200320163001728.png)
- 仔细看`org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`类
@ -213,13 +213,13 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200320163806852](../../../images/SpringBoot/image-20200320163806852.png)
![image-20200320163806852](../../images/SpringBoot/image-20200320163806852.png)
再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作
关注 `AnnotationMetadata annotationMetadata` 存储了一些什么
![image-20200320164145286](../../../images/SpringBoot/image-20200320164145286.png)
![image-20200320164145286](../../images/SpringBoot/image-20200320164145286.png)
这里简单理解
@ -271,7 +271,7 @@ public class RedisProperties {
```
![image-20200320171138431](../../../images/SpringBoot/image-20200320171138431.png)
![image-20200320171138431](../../images/SpringBoot/image-20200320171138431.png)
### getCandidateConfigurations
@ -289,7 +289,7 @@ public class RedisProperties {
```
![image-20200320171734270](../../../images/SpringBoot/image-20200320171734270.png)
![image-20200320171734270](../../images/SpringBoot/image-20200320171734270.png)
- 第一个是我自己写的一个测试用
@ -341,7 +341,7 @@ public class RedisProperties {
```
![image-20200323080611527](../../../images/SpringBoot/image-20200323080611527.png)
![image-20200323080611527](../../images/SpringBoot/image-20200323080611527.png)
- 修改启动类
@ -350,7 +350,7 @@ public class RedisProperties {
```
![image-20200323081009823](../../../images/SpringBoot/image-20200323081009823.png)
![image-20200323081009823](../../images/SpringBoot/image-20200323081009823.png)
### checkExcludedClasses
@ -418,7 +418,7 @@ public class RedisProperties {
- `getAutoConfigurationImportFilters()` 从`spring.factories` 获取 `AutoConfigurationImportFilter`的接口
![image-20200323081903145](../../../images/SpringBoot/image-20200323081903145.png)
![image-20200323081903145](../../images/SpringBoot/image-20200323081903145.png)
- 循环内执行`Aware`系列接口
@ -426,7 +426,7 @@ public class RedisProperties {
- `filter.match(candidates, autoConfigurationMetadata)` 比较判断哪些是需要自动注入的类
![image-20200323082553595](../../../images/SpringBoot/image-20200323082553595.png)
![image-20200323082553595](../../images/SpringBoot/image-20200323082553595.png)
### fireAutoConfigurationImportEvents
@ -448,11 +448,11 @@ public class RedisProperties {
```
![image-20200323083149737](../../../images/SpringBoot/image-20200323083149737.png)
![image-20200323083149737](../../images/SpringBoot/image-20200323083149737.png)
- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);`
![image-20200323083247061](../../../images/SpringBoot/image-20200323083247061.png)
![image-20200323083247061](../../images/SpringBoot/image-20200323083247061.png)
- `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`**
@ -470,7 +470,7 @@ public class RedisProperties {
```
![image-20200323083656670](../../../images/SpringBoot/image-20200323083656670.png)
![image-20200323083656670](../../images/SpringBoot/image-20200323083656670.png)
- 初始化完
@ -478,7 +478,7 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200323084922159](../../../images/SpringBoot/image-20200323084922159.png)
![image-20200323084922159](../../images/SpringBoot/image-20200323084922159.png)
- 后续的一些行为相对简单,直接放个源码了.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Loading…
Cancel
Save