图片在Typora里面无法查看,修改图片的相对路径

pull/53/head
HendSame 5 years ago
parent 141ecfd770
commit 068196e57d

@ -11,7 +11,7 @@ dubbo自己实现了一套SPI机制并对 JDK的SPI进行了改进。
下面我们看一下Dubbo 的 SPI扩展机制实现的结构目录。 下面我们看一下Dubbo 的 SPI扩展机制实现的结构目录。
![avatar](/images/Dubbo/SPI组件目录结构.png) ![avatar](../../../images/Dubbo/SPI组件目录结构.png)
### SPI 注解 ### SPI 注解
首先看一下 SPI注解。在某个接口上加上 @SPI 注解后表明该接口为可扩展接口。比如协议扩展接口Protocol如果使用者在 <dubbo:protocol />、<dubbo:service />、<dubbo:reference /> 都没有指定 protocol属性 的话,那么就默认使用 DubboProtocol 作为接口Protocol的实现因为在 Protocol 上有 @SPI("dubbo")注解。而这个 protocol属性值 或者默认值会被当作该接口的实现类中的一个keydubbo 会去 META-INF.dubbo.internal下的com.alibaba.dubbo.rpc.Protocol文件中找该key对应的value源码如下。 首先看一下 SPI注解。在某个接口上加上 @SPI 注解后表明该接口为可扩展接口。比如协议扩展接口Protocol如果使用者在 <dubbo:protocol />、<dubbo:service />、<dubbo:reference /> 都没有指定 protocol属性 的话,那么就默认使用 DubboProtocol 作为接口Protocol的实现因为在 Protocol 上有 @SPI("dubbo")注解。而这个 protocol属性值 或者默认值会被当作该接口的实现类中的一个keydubbo 会去 META-INF.dubbo.internal下的com.alibaba.dubbo.rpc.Protocol文件中找该key对应的value源码如下。

@ -1,7 +1,7 @@
## 项目结构 ## 项目结构
首先从GitHub 上 down下来Dubbo项目我们根据里面的目录名 也能大概猜出来各个模块的作用。 首先从GitHub 上 down下来Dubbo项目我们根据里面的目录名 也能大概猜出来各个模块的作用。
![avatar](/images/Dubbo/dubbo项目结构.png) ![avatar](../../../images/Dubbo/dubbo项目结构.png)
### dubbo-­common ### dubbo-­common
公共逻辑模块,定义了各模块中 通用的 组件 和 工具类IO、日志、配置处理等。 公共逻辑模块,定义了各模块中 通用的 组件 和 工具类IO、日志、配置处理等。
@ -34,7 +34,7 @@ dubbo配置模块该模块通过 配置信息将dubbo组件的各个模块
其运行原理如下图所示。 其运行原理如下图所示。
![avatar](/Dubbo工作原理图.png) ![avatar](../../../images/Dubbo/Dubbo工作原理图.png)
### 工作原理 ### 工作原理
最后总结下其工作原理。 最后总结下其工作原理。

@ -13,12 +13,12 @@
下面我们来看一下 集群模块的项目结构图,结合上文的描述,可以对其有更加深刻的理解。 下面我们来看一下 集群模块的项目结构图,结合上文的描述,可以对其有更加深刻的理解。
![avatar](/images/Dubbo/dubbo-cluster模块工程结构.png) ![avatar](../../../images/Dubbo/dubbo-cluster模块工程结构.png)
### 集群模块核心API 源码解析 ### 集群模块核心API 源码解析
从上图应该也能看出其核心API在哪个包里。 从上图应该也能看出其核心API在哪个包里。
![avatar](/images/Dubbo/com.alibaba.dubbo.rpc.cluster包目录.png) ![avatar](../../../images/Dubbo/com.alibaba.dubbo.rpc.cluster包目录.png)
各核心接口的源码如下。 各核心接口的源码如下。
```java ```java

@ -1,21 +1,21 @@
## 注册中心在Dubbo中的作用 ## 注册中心在Dubbo中的作用
服务治理框架可以大致分为 服务通信 和 服务管理 两部分服务管理可以分为服务注册、服务订阅以及服务发现服务提供者Provider 会往注册中心注册服务而消费者Consumer 会从注册中心中订阅自己关注的服务,并在关注的服务发生变更时 得到注册中心的通知。Provider、Consumer以及Registry之间的依赖关系 如下图所示。 服务治理框架可以大致分为 服务通信 和 服务管理 两部分服务管理可以分为服务注册、服务订阅以及服务发现服务提供者Provider 会往注册中心注册服务而消费者Consumer 会从注册中心中订阅自己关注的服务,并在关注的服务发生变更时 得到注册中心的通知。Provider、Consumer以及Registry之间的依赖关系 如下图所示。
![avatar](/images/Dubbo/Dubbo工作原理图.png) ![avatar](../../../images/Dubbo/Dubbo工作原理图.png)
## dubbo-registry 模块 结构分析 ## dubbo-registry 模块 结构分析
dubbo的注册中心有多种实现方案zookeeper、redis、multicast等本章先看一下 dubbo-registry 模块的核心部分 dubbo-registry-api具体实现部分放到下章来讲。dubbo-registry模块 的结构如下图所示。 dubbo的注册中心有多种实现方案zookeeper、redis、multicast等本章先看一下 dubbo-registry 模块的核心部分 dubbo-registry-api具体实现部分放到下章来讲。dubbo-registry模块 的结构如下图所示。
![avatar](/images/Dubbo/dubbo-registry模块结构图.png) ![avatar](../../../images/Dubbo/dubbo-registry模块结构图.png)
### Registry 核心组件类图 ### Registry 核心组件类图
典型的 接口 -> 抽象类 -> 实现类 的结构设计,如下图所示。 典型的 接口 -> 抽象类 -> 实现类 的结构设计,如下图所示。
![avatar](/images/Dubbo/Registry组件类图.png) ![avatar](../../../images/Dubbo/Registry组件类图.png)
既然有Registry组件那么按照很多框架的套路肯定也有一个用于获取 Registry实例的RegistryFactory其中用到了工厂方法模式不同的工厂类用于获取不同类型的实例。其类图结构如下。 既然有Registry组件那么按照很多框架的套路肯定也有一个用于获取 Registry实例的RegistryFactory其中用到了工厂方法模式不同的工厂类用于获取不同类型的实例。其类图结构如下。
![avatar](/images/Dubbo/RegistryFactory组件类图.png) ![avatar](../../../images/Dubbo/RegistryFactory组件类图.png)
## 源码详解 ## 源码详解
根据上面的类图,我们开始从上往下 详解dubbo中对于注册中心的设计以及实现。 根据上面的类图,我们开始从上往下 详解dubbo中对于注册中心的设计以及实现。

@ -2,7 +2,7 @@ Dubbo的注册中心 虽然提供了多种实现,但生产上的事实标准
由于 Dubbo 是一个分布式RPC开源框架各服务之间单独部署往往会出现资源之间数据不一致的问题比如某一个服务增加或减少了几台机器某个服务提供者变更了服务地址那么服务消费者是很难感知到这种变化的。而 Zookeeper 本身就有保证分布式数据一致性的特性。那么 Dubbo服务是如何被 Zookeeper的数据结构存储管理的呢zookeeper采用的是树形结构来组织数据节点它类似于一个标准的文件系统如下图所示。 由于 Dubbo 是一个分布式RPC开源框架各服务之间单独部署往往会出现资源之间数据不一致的问题比如某一个服务增加或减少了几台机器某个服务提供者变更了服务地址那么服务消费者是很难感知到这种变化的。而 Zookeeper 本身就有保证分布式数据一致性的特性。那么 Dubbo服务是如何被 Zookeeper的数据结构存储管理的呢zookeeper采用的是树形结构来组织数据节点它类似于一个标准的文件系统如下图所示。
![avatar](/images/Dubbo/dubbo注册中心在zookeeper中的结构.png) ![avatar](../../../images/Dubbo/dubbo注册中心在zookeeper中的结构.png)
该图展示了dubbo在zookeeper中存储的形式以及节点层级。dubbo的Root层是根目录通过<dubbo:registry group="dubbo" />的“group”来设置zookeeper的根节点缺省值是“dubbo”。Service层是服务接口的全名。Type层是分类一共有四种分类分别是providers 服务提供者列表、consumers 服务消费者列表、routes 路由规则列表、configurations 配置规则列表。URL层 根据不同的Type目录可以有服务提供者 URL 、服务消费者 URL 、路由规则 URL 、配置规则 URL 。不同的Type关注的URL不同。 该图展示了dubbo在zookeeper中存储的形式以及节点层级。dubbo的Root层是根目录通过<dubbo:registry group="dubbo" />的“group”来设置zookeeper的根节点缺省值是“dubbo”。Service层是服务接口的全名。Type层是分类一共有四种分类分别是providers 服务提供者列表、consumers 服务消费者列表、routes 路由规则列表、configurations 配置规则列表。URL层 根据不同的Type目录可以有服务提供者 URL 、服务消费者 URL 、路由规则 URL 、配置规则 URL 。不同的Type关注的URL不同。
@ -10,7 +10,7 @@ zookeeper以斜杠来分割每一层的znode节点比如第一层根节点dub
dubbo-registry-zookeeper 模块的工程结构如下图所示,里面就俩类,非常简单。 dubbo-registry-zookeeper 模块的工程结构如下图所示,里面就俩类,非常简单。
![avatar](/images/Dubbo/dubbo-registry-zookeeper模块工程结构图.png) ![avatar](../../../images/Dubbo/dubbo-registry-zookeeper模块工程结构图.png)
### ZookeeperRegistry ### ZookeeperRegistry
该类继承了FailbackRegistry抽象类针对注册中心核心的 服务注册、服务订阅、取消注册、取消订阅,查询注册列表进行展开,这里用到了 模板方法设计模式FailbackRegistry中定义了register()、subscribe()等模板方法和 doRegister()、doSubscribe()抽象方法ZookeeperRegistry基于zookeeper对这些抽象方法进行了实现。其实你会发现zookeeper虽然是最被推荐的反而它的实现逻辑相对简单因为调用了zookeeper服务组件很多的逻辑不需要在dubbo中自己去实现。 该类继承了FailbackRegistry抽象类针对注册中心核心的 服务注册、服务订阅、取消注册、取消订阅,查询注册列表进行展开,这里用到了 模板方法设计模式FailbackRegistry中定义了register()、subscribe()等模板方法和 doRegister()、doSubscribe()抽象方法ZookeeperRegistry基于zookeeper对这些抽象方法进行了实现。其实你会发现zookeeper虽然是最被推荐的反而它的实现逻辑相对简单因为调用了zookeeper服务组件很多的逻辑不需要在dubbo中自己去实现。

@ -1,7 +1,7 @@
## 线程池核心组件图解 ## 线程池核心组件图解
看源码之前,先了解一下该组件 最主要的几个 接口、抽象类和实现类的结构关系。 看源码之前,先了解一下该组件 最主要的几个 接口、抽象类和实现类的结构关系。
![avatar](/images/JDK1.8/线程池组件类图.png) ![avatar](../../images/JDK1.8/线程池组件类图.png)
该组件中Executor 和 ExecutorService接口 定义了线程池最核心的几个方法提交任务submit 该组件中Executor 和 ExecutorService接口 定义了线程池最核心的几个方法提交任务submit
()、关闭线程池shutdown()。抽象类 AbstractExecutorService 主要对公共行为 submit()系列方法进行了实现,这些 submit()方法 的实现使用了 模板方法模式,其中调用的 execute()方法 是未实现的 来自 Executor接口 的方法。实现类 ThreadPoolExecutor 则对线程池进行了具体而复杂的实现。 ()、关闭线程池shutdown()。抽象类 AbstractExecutorService 主要对公共行为 submit()系列方法进行了实现,这些 submit()方法 的实现使用了 模板方法模式,其中调用的 execute()方法 是未实现的 来自 Executor接口 的方法。实现类 ThreadPoolExecutor 则对线程池进行了具体而复杂的实现。
@ -211,7 +211,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
``` ```
ThreadPoolExecutor 中的 execute()方法 执行 Runnable任务 的流程逻辑可以用下图表示。 ThreadPoolExecutor 中的 execute()方法 执行 Runnable任务 的流程逻辑可以用下图表示。
![avatar](/images/ConcurrentProgramming/线程池流程.png) ![avatar](../../../images/ConcurrentProgramming/线程池流程.png)
### 工具类 Executors ### 工具类 Executors
看类名也知道,它最主要的作用就是提供 static 的工具方法,为开发者提供各种封装好的 具有各自特性的线程池。 看类名也知道,它最主要的作用就是提供 static 的工具方法,为开发者提供各种封装好的 具有各自特性的线程池。

@ -1,11 +1,11 @@
## 类图结构 ## 类图结构
J.U.C 的锁组件中 类相对较少从JDK相应的包中也能看出来下图标记了其中最主要的几个接口和类也是本文要分析的重点。 J.U.C 的锁组件中 类相对较少从JDK相应的包中也能看出来下图标记了其中最主要的几个接口和类也是本文要分析的重点。
![avatar](/images/JDK1.8/JUC的locks包.png) ![avatar](../../images/JDK1.8/JUC的locks包.png)
下图 将这几个接口和类 以类图的方式展现出来,其中包含了它们所声明的主要方法。 下图 将这几个接口和类 以类图的方式展现出来,其中包含了它们所声明的主要方法。
![avatar](/images/JDK1.8/JUC锁组件类图.png) ![avatar](../../images/JDK1.8/JUC锁组件类图.png)
## Lock 组件 ## Lock 组件
Lock 组件的结构很简单,只有一个接口和一个实现类,源码如下。 Lock 组件的结构很简单,只有一个接口和一个实现类,源码如下。

@ -317,4 +317,4 @@ public class Thread implements Runnable {
``` ```
之前一直对线程状态 及 状态切换的概念模糊不清,现在通过源码中对线程状态的定义,我们可以画张图来重新回顾一下,以使我们对其有更加深刻的理解。 之前一直对线程状态 及 状态切换的概念模糊不清,现在通过源码中对线程状态的定义,我们可以画张图来重新回顾一下,以使我们对其有更加深刻的理解。
![avatar](/images/JDK1.8/ThreadStatusChange.png) ![avatar](../../images/JDK1.8/ThreadStatusChange.png)

@ -256,7 +256,7 @@ public class ThreadLocal<T> {
``` ```
简单画个图总结一下 ThreadLocal 的原理,如下。 简单画个图总结一下 ThreadLocal 的原理,如下。
![avatar](/images/JDK1.8/ThreadLocal原理.png) ![avatar](../../images/JDK1.8/ThreadLocal原理.png)
最后强调一下 ThreadLocal的使用注意事项 最后强调一下 ThreadLocal的使用注意事项

@ -28,6 +28,7 @@ public class Singleton3 {
* 使用volatile修饰instance变量 可以 避免上述的指令重排 * 使用volatile修饰instance变量 可以 避免上述的指令重排
* tips不太理解的是 第一个线程在执行第2步之前就已经释放了锁吗导致其它线程进入synchronized代码块 * tips不太理解的是 第一个线程在执行第2步之前就已经释放了锁吗导致其它线程进入synchronized代码块
* 执行 instance == null 的判断? * 执行 instance == null 的判断?
* 回答第一个线程在执行第2步之前就已经释放了锁吗没有。如果不使用volatile修饰instance变量那么其他线程进来的时候看到的instance就有可能不是null的因为已经执行了第3步那么此时这个线程执行 return instance;使用的instance是一个没有初始化的instance就会有问题。
*/ */
private volatile static Singleton3 instance; private volatile static Singleton3 instance;
@ -485,7 +486,7 @@ public class SmartTransformerFactoryImpl extends SAXTransformerFactory {
### 个人理解 ### 个人理解
该模式主要用于将复杂对象的构建过程分解成一个个简单的步骤,或者分摊到多个类中进行构建,保证构建过程层次清晰,代码不会过分臃肿,屏蔽掉了复杂对象内部的具体构建细节,其类图结构如下所示。 该模式主要用于将复杂对象的构建过程分解成一个个简单的步骤,或者分摊到多个类中进行构建,保证构建过程层次清晰,代码不会过分臃肿,屏蔽掉了复杂对象内部的具体构建细节,其类图结构如下所示。
![avatar](/images/DesignPattern/建造者模式类图.png) ![avatar](../../../images/DesignPattern/建造者模式类图.png)
该模式的主要角色如下: 该模式的主要角色如下:

@ -812,7 +812,7 @@ class PooledConnection implements InvocationHandler {
装饰器模式能够帮助我们解决上述问题,装饰器可以动态地为对象添加功能,它是基于组合的方式实现该功能的。在实践中,我们应该尽量使用组合的方式来扩展系统的功能,而非使用继承的方式。通过装饰器模式的介绍,可以帮助读者更好地理解设计模式中常见的一句话:组合优于继承。下面先来看一下装饰器模式的类图,及其核心角色。 装饰器模式能够帮助我们解决上述问题,装饰器可以动态地为对象添加功能,它是基于组合的方式实现该功能的。在实践中,我们应该尽量使用组合的方式来扩展系统的功能,而非使用继承的方式。通过装饰器模式的介绍,可以帮助读者更好地理解设计模式中常见的一句话:组合优于继承。下面先来看一下装饰器模式的类图,及其核心角色。
![avatar](/images/DesignPattern/装饰器模式类图.png) ![avatar](../../../images/DesignPattern/装饰器模式类图.png)
- Component (组件):组件接口定义了全部 “组件实现类” 以及所有 “装饰器实现” 的行为。 - Component (组件):组件接口定义了全部 “组件实现类” 以及所有 “装饰器实现” 的行为。
- ConcreteComponent (具体组件实现类):通常情况下,具体组件实现类就是被装饰器装饰的原始对象,该类提供了 Component 接口中定义的最基本的功能,其他高级功能或后续添加的新功能,都是通过装饰器的方式添加到该类的对象之上的。 - ConcreteComponent (具体组件实现类):通常情况下,具体组件实现类就是被装饰器装饰的原始对象,该类提供了 Component 接口中定义的最基本的功能,其他高级功能或后续添加的新功能,都是通过装饰器的方式添加到该类的对象之上的。

@ -7,7 +7,7 @@
#### 个人理解 #### 个人理解
去年看了蛮多源码,发现 框架的开发者在实际使用设计模式时,大都会根据实际情况 使用其变体,老老实实按照书上的类图及定义去设计代码的比较少。不过我们依然还是先看一下书上的定义,然后比较一下理论与实践的一些差别吧。策略模式的类图及定义如下。 去年看了蛮多源码,发现 框架的开发者在实际使用设计模式时,大都会根据实际情况 使用其变体,老老实实按照书上的类图及定义去设计代码的比较少。不过我们依然还是先看一下书上的定义,然后比较一下理论与实践的一些差别吧。策略模式的类图及定义如下。
![avatar](/images/DesignPattern/策略模式类图.png) ![avatar](../../../images/DesignPattern/策略模式类图.png)
定义一系列算法,封装每个算法 并使它们可以互换。该模式的主要角色如下: 定义一系列算法,封装每个算法 并使它们可以互换。该模式的主要角色如下:
@ -948,7 +948,7 @@ public class ArrayList<E> extends AbstractList<E>
#### 个人理解 #### 个人理解
这个模式也是平时很少使用的所以就简单介绍一下然后结合JDK中的源码加深理解。该模式用于定义对象之间的一对多依赖当一个对象状态改变时它的所有依赖都会收到通知然后自动更新。类图和主要角色如下 这个模式也是平时很少使用的所以就简单介绍一下然后结合JDK中的源码加深理解。该模式用于定义对象之间的一对多依赖当一个对象状态改变时它的所有依赖都会收到通知然后自动更新。类图和主要角色如下
![avatar](/images/DesignPattern/观察者模式类图.png) ![avatar](../../../images/DesignPattern/观察者模式类图.png)
- Subject主题具有注册、移除及通知观察者的功能主题是通过维护一个观察者列表来实现这些功能的 - Subject主题具有注册、移除及通知观察者的功能主题是通过维护一个观察者列表来实现这些功能的
- Observer观察者其注册需要Subject的registerObserver()方法。 - Observer观察者其注册需要Subject的registerObserver()方法。
@ -1080,11 +1080,11 @@ public class Observable {
在责任链模式中,将上述臃肿的请求处理逻辑 拆分到多个 功能逻辑单一的 Handler 处理类中,这样我们就可以根据业务需求,将多个 Handler 对象组合成一条责任链,实现请求的处理。在一条责任链中,每个 Handler对象 都包含对下一个 Handler对象 的引用,一个 Handler对象 处理完请求消息(或不能处理该请求)时, 会把请求传给下一个 Handler对象 继续处理,依此类推,直至整条责任链结束。简单看一下责任链模式的类图。 在责任链模式中,将上述臃肿的请求处理逻辑 拆分到多个 功能逻辑单一的 Handler 处理类中,这样我们就可以根据业务需求,将多个 Handler 对象组合成一条责任链,实现请求的处理。在一条责任链中,每个 Handler对象 都包含对下一个 Handler对象 的引用,一个 Handler对象 处理完请求消息(或不能处理该请求)时, 会把请求传给下一个 Handler对象 继续处理,依此类推,直至整条责任链结束。简单看一下责任链模式的类图。
![avatar](/images/DesignPattern/责任链模式.png) ![avatar](../../../images/DesignPattern/责任链模式.png)
#### Netty 中的应用 #### Netty 中的应用
在 Netty 中,将 Channel 的数据管道抽象为 ChannelPipeline消息在 ChannelPipeline 中流动和传递。ChannelPipeline 是 ChannelHandler 的容器,持有 I/O事件拦截器 ChannelHandler 的链表,负责对 ChannelHandler 的管理和调度。由 ChannelHandler 对 I/O事件 进行拦截和处理,并可以通过接口方便地新增和删除 ChannelHandler 来实现不同业务逻辑的处理。下图是 ChannelPipeline源码中描绘的责任链事件处理过程。 在 Netty 中,将 Channel 的数据管道抽象为 ChannelPipeline消息在 ChannelPipeline 中流动和传递。ChannelPipeline 是 ChannelHandler 的容器,持有 I/O事件拦截器 ChannelHandler 的链表,负责对 ChannelHandler 的管理和调度。由 ChannelHandler 对 I/O事件 进行拦截和处理,并可以通过接口方便地新增和删除 ChannelHandler 来实现不同业务逻辑的处理。下图是 ChannelPipeline源码中描绘的责任链事件处理过程。
![avatar](/images/Netty/ChannelPipeline责任链事件处理过程.png) ![avatar](../../../images/Netty/ChannelPipeline责任链事件处理过程.png)
其具体过程处理如下: 其具体过程处理如下:
1. 底层SocketChannel 的 read方法 读取 ByteBuf触发 ChannelRead事件由 I/O线程 NioEventLoop 调用 ChannelPipeline 的 fireChannelRead()方法,将消息传输到 ChannelPipeline中。 1. 底层SocketChannel 的 read方法 读取 ByteBuf触发 ChannelRead事件由 I/O线程 NioEventLoop 调用 ChannelPipeline 的 fireChannelRead()方法,将消息传输到 ChannelPipeline中。

@ -339,7 +339,7 @@ public class PoolState {
PooledDataSource 管理的数据库连接对象 是由其持有的 UnpooledDataSource对象 创建的,并由 PoolState 管理所有连接的状态。 PooledDataSource 管理的数据库连接对象 是由其持有的 UnpooledDataSource对象 创建的,并由 PoolState 管理所有连接的状态。
PooledDataSource 的 getConnection()方法 会首先调用 popConnection()方法 获取 PooledConnection对象然后通过 PooledConnection 的 getProxyConnection()方法 获取数据库连接的代理对象。popConnection()方法 是 PooledDataSource 的核心逻辑之一,其整体的逻辑关系如下图: PooledDataSource 的 getConnection()方法 会首先调用 popConnection()方法 获取 PooledConnection对象然后通过 PooledConnection 的 getProxyConnection()方法 获取数据库连接的代理对象。popConnection()方法 是 PooledDataSource 的核心逻辑之一,其整体的逻辑关系如下图:
![avatar](/images/mybatis/数据库连接池流程图.png) ![avatar](../../../images/mybatis/数据库连接池流程图.png)
```java ```java
public class PooledDataSource implements DataSource { public class PooledDataSource implements DataSource {

@ -47,7 +47,7 @@ public interface Cache {
``` ```
如下图所示Cache接口 的实现类有很多,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口 的基本实现。 如下图所示Cache接口 的实现类有很多,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口 的基本实现。
![avatar](/images/mybatis/Cache组件.png) ![avatar](../../../images/mybatis/Cache组件.png)
### 1.1 PerpetualCache ### 1.1 PerpetualCache
PerpetualCachePerpetual永恒的持续的在缓存模块中扮演着被装饰的角色其实现比较简单底层使用 HashMap 记录缓存项,也是通过该 HashMap对象 的方法实现的 Cache接口 中定义的相应方法。 PerpetualCachePerpetual永恒的持续的在缓存模块中扮演着被装饰的角色其实现比较简单底层使用 HashMap 记录缓存项,也是通过该 HashMap对象 的方法实现的 Cache接口 中定义的相应方法。

@ -143,13 +143,13 @@ class HfReflectorTest {
- 准备工作完成了开始进行 debug , 在`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`这个方法上打上断点 - 准备工作完成了开始进行 debug , 在`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`这个方法上打上断点
![1575890354400](/images/mybatis/1575890354400.png) ![1575890354400](../../../images/mybatis/1575890354400.png)
观察`constructors`属性存在两个方法,这两个方法就是我在`People`类中的构造方法. 观察`constructors`属性存在两个方法,这两个方法就是我在`People`类中的构造方法.
根据语法内容我们应该对`parameterTypes`属性进行查看 根据语法内容我们应该对`parameterTypes`属性进行查看
![1575890475839](/images/mybatis/1575890475839.png) ![1575890475839](../../../images/mybatis/1575890475839.png)
可以发现空参构造的`parameterTypes`长度是0.因此可以确认`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`方法获取了空参构造 可以发现空参构造的`parameterTypes`长度是0.因此可以确认`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`方法获取了空参构造
@ -287,17 +287,17 @@ class HfReflectorTest {
- 照旧我们进行 debug 当前方法为`toString`方法 - 照旧我们进行 debug 当前方法为`toString`方法
![1575891988804](/images/mybatis//1575891988804.png) ![1575891988804](../../../images/mybatis//1575891988804.png)
从返回结果可以看到`sb.toString`返回的是: `返回值类型#方法名` 从返回结果可以看到`sb.toString`返回的是: `返回值类型#方法名`
![1575892046692](/images/mybatis//1575892046692.png) ![1575892046692](../../../images/mybatis//1575892046692.png)
上图返回结果为`void#setName:java.lang.String` 命名规则:`返回值类型#方法名称:参数列表` 上图返回结果为`void#setName:java.lang.String` 命名规则:`返回值类型#方法名称:参数列表`
回过头看看`uniqueMethods`里面是什么 回过头看看`uniqueMethods`里面是什么
![1575892167982](/images/mybatis//1575892167982.png) ![1575892167982](../../../images/mybatis//1575892167982.png)
方法签名:方法 方法签名:方法
@ -325,11 +325,11 @@ class HfReflectorTest {
目标明确了就直接在 目标明确了就直接在
![1575892414120](/images/mybatis//1575892414120.png) ![1575892414120](../../../images/mybatis//1575892414120.png)
这里打断点了 这里打断点了
![1575892511471](/images/mybatis//1575892511471.png) ![1575892511471](../../../images/mybatis//1575892511471.png)
在进入循环之前回率先加载本类的所有可见方法 在进入循环之前回率先加载本类的所有可见方法
@ -342,15 +342,15 @@ class HfReflectorTest {
接下来断点继续往下走 接下来断点继续往下走
![1575892645405](/images/mybatis//1575892645405.png) ![1575892645405](../../../images/mybatis//1575892645405.png)
走到这一步我们来看看`currentClass.getSuperclass()`是不是上一级的类 走到这一步我们来看看`currentClass.getSuperclass()`是不是上一级的类
![1575892687076](/images/mybatis//1575892687076.png) ![1575892687076](../../../images/mybatis//1575892687076.png)
通过断点可见这个`currentClass`现在是`People`类,根据之前所说的最终`uniqueMethods`应该存在父类的方法 通过断点可见这个`currentClass`现在是`People`类,根据之前所说的最终`uniqueMethods`应该存在父类的方法
![1575892763661](/images/mybatis//1575892763661.png) ![1575892763661](../../../images/mybatis//1575892763661.png)
可以看到父类的方法也都存在了 可以看到父类的方法也都存在了
@ -431,4 +431,4 @@ class HfReflectorTest {
- 下图为一个类的解析结果 - 下图为一个类的解析结果
![1575894218362](/images/mybatis/1575894218362.png) ![1575894218362](../../../images/mybatis/1575894218362.png)

@ -81,7 +81,7 @@ public interface SqlSession extends Closeable {
### 1.1 DefaultSqlSession ### 1.1 DefaultSqlSession
DefaultSqlSession是单独使用MyBatis进行开发时最常用的SqISession接口实现。其实现了SqISession接口中定义的方法及各方法的重载。select()系列方法、selectOne()系列方法、selectList()系列方法、selectMap()系列方法之间的调用关系如下图殊途同归它们最终都会调用Executor的query()方法。 DefaultSqlSession是单独使用MyBatis进行开发时最常用的SqISession接口实现。其实现了SqISession接口中定义的方法及各方法的重载。select()系列方法、selectOne()系列方法、selectList()系列方法、selectMap()系列方法之间的调用关系如下图殊途同归它们最终都会调用Executor的query()方法。
![avatar](/images/mybatis/DefaultSqlSession方法调用栈.png) ![avatar](../../../images/mybatis/DefaultSqlSession方法调用栈.png)
上述重载方法最终都是通过调用Executor的query(MappedStatement, Object, RowBounds,ResultHandler)方法实现数据库查询操作的但各自对结果对象进行了相应的调整例如selectOne()方法是从结果对象集合中获取了第一个元素返回selectMap()方法会将List类型的结果集 转换成Map类型集合返回select()方法是将结果集交由用户指定的ResultHandler对象处理且没有返回值selectList()方法则是直接返回结果对象集合。 上述重载方法最终都是通过调用Executor的query(MappedStatement, Object, RowBounds,ResultHandler)方法实现数据库查询操作的但各自对结果对象进行了相应的调整例如selectOne()方法是从结果对象集合中获取了第一个元素返回selectMap()方法会将List类型的结果集 转换成Map类型集合返回select()方法是将结果集交由用户指定的ResultHandler对象处理且没有返回值selectList()方法则是直接返回结果对象集合。
DefaultSqlSession的insert()方法、update()方法、delete()方法也有多个重载它们最后都是通过调用DefaultSqlSession的update(String, Object)方法实现的该重载首先会将dirty字段置为true然后再通过Executor的update()方法完成数据库修改操作。 DefaultSqlSession的insert()方法、update()方法、delete()方法也有多个重载它们最后都是通过调用DefaultSqlSession的update(String, Object)方法实现的该重载首先会将dirty字段置为true然后再通过Executor的update()方法完成数据库修改操作。

@ -29,7 +29,7 @@ public interface DataSourceFactory {
类图如下 类图如下
![image-20191223081023730](/images/mybatis/image-20191223081023730.png) ![image-20191223081023730](../../../images/mybatis/image-20191223081023730.png)
- `setProperties`会将下列标签放入`datasource`中 - `setProperties`会将下列标签放入`datasource`中
@ -352,9 +352,9 @@ public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
从类图上或者代码中我们可以发现`PooledDataSourceFactory`是继承`UnpooledDataSourceFactory`那么方法应该也是`UnpooledDataSourceFactory`的。看看设置属性方法 从类图上或者代码中我们可以发现`PooledDataSourceFactory`是继承`UnpooledDataSourceFactory`那么方法应该也是`UnpooledDataSourceFactory`的。看看设置属性方法
![image-20191223083610214](/images/mybatis/image-20191223083610214.png) ![image-20191223083610214](../../../images/mybatis/image-20191223083610214.png)
方法直接走完 方法直接走完
![image-20191223083732972](/images/mybatis/image-20191223083732972.png) ![image-20191223083732972](../../../images/mybatis/image-20191223083732972.png)

@ -187,7 +187,7 @@
HsSell[] list(@Param("ID") Integer id); HsSell[] list(@Param("ID") Integer id);
``` ```
![image-20191219092442456](/images/mybatis/image-20191219092442456.png) ![image-20191219092442456](../../../images/mybatis/image-20191219092442456.png)
- 修改mapper,对`org.apache.ibatis.binding.MapperMethod#convertToDeclaredCollection`进行测试 - 修改mapper,对`org.apache.ibatis.binding.MapperMethod#convertToDeclaredCollection`进行测试
@ -197,7 +197,7 @@
![image-20191219093043035](/images/mybatis/image-20191219093043035.png) ![image-20191219093043035](../../../images/mybatis/image-20191219093043035.png)

@ -5,7 +5,7 @@
类图: 类图:
![image-20191223100956713](/images/mybatis/image-20191223100956713.png) ![image-20191223100956713](../../../images/mybatis/image-20191223100956713.png)
```java ```java
public interface ObjectWrapper { public interface ObjectWrapper {

@ -176,9 +176,9 @@ public class ParamNameResolver {
- 写`@Param`返回 - 写`@Param`返回
![image-20191219083344439](/images/mybatis/image-20191219083344439.png) ![image-20191219083344439](../../../images/mybatis/image-20191219083344439.png)
![image-20191219083354873](/images/mybatis/image-20191219083354873.png) ![image-20191219083354873](../../../images/mybatis/image-20191219083354873.png)
@ -188,7 +188,7 @@ public class ParamNameResolver {
List<HsSell> list( Integer id); List<HsSell> list( Integer id);
``` ```
![image-20191219084455292](/images/mybatis/image-20191219084455292.png) ![image-20191219084455292](../../../images/mybatis/image-20191219084455292.png)
@ -199,6 +199,6 @@ public class ParamNameResolver {
写上`@Param` 写上`@Param`
![image-20191219084943102](/images/mybatis/image-20191219084943102.png) ![image-20191219084943102](../../../images/mybatis/image-20191219084943102.png)
![image-20191219085131167](/images/mybatis/image-20191219085131167.png) ![image-20191219085131167](../../../images/mybatis/image-20191219085131167.png)

@ -93,11 +93,11 @@
``` ```
![image-20191218191512184](/images/mybatis/image-20191218191512184.png) ![image-20191218191512184](../../../images/mybatis/image-20191218191512184.png)
![image-20191218191550550](/images/mybatis/image-20191218191550550.png) ![image-20191218191550550](../../../images/mybatis/image-20191218191550550.png)

@ -173,4 +173,4 @@ public class GenericTokenParser {
``` ```
![image-20191219100446796](/images/mybatis/image-20191219100446796.png) ![image-20191219100446796](../../../images/mybatis/image-20191219100446796.png)

@ -4,7 +4,7 @@
## Netty的三层架构设计 ## Netty的三层架构设计
Netty 采用了典型的三层网络架构进行设计和开发,其逻辑架构图如下所示。 Netty 采用了典型的三层网络架构进行设计和开发,其逻辑架构图如下所示。
![avatar](/images/Netty/Netty逻辑架构图.png) ![avatar](../../../images/Netty/Netty逻辑架构图.png)
### 通信调度层 Reactor ### 通信调度层 Reactor
它由一系列辅助类完成,包括 Reactor线程 NioEventLoop 及其父类NioSocketChannel / NioServerSocketChannel 及其父类Buffer组件Unsafe组件 等。该层的主要职责就是**监听网络的读写和连接操作**,负责**将网络层的数据读取到内存缓冲区**,然后触发各种网络事件,例如连接创建、连接激活、读事件、写事件等,将这些事件触发到 PipeLine 中,由 PipeLine 管理的责任链来进行后续的处理。 它由一系列辅助类完成,包括 Reactor线程 NioEventLoop 及其父类NioSocketChannel / NioServerSocketChannel 及其父类Buffer组件Unsafe组件 等。该层的主要职责就是**监听网络的读写和连接操作**,负责**将网络层的数据读取到内存缓冲区**,然后触发各种网络事件,例如连接创建、连接激活、读事件、写事件等,将这些事件触发到 PipeLine 中,由 PipeLine 管理的责任链来进行后续的处理。

@ -98,7 +98,7 @@ Netty 主从多线程模型 代码示例如下。
为了尽可能提升性能Netty 对消息的处理 采用了串行无锁化设计,在 I/O线程 内部进行串行操作避免多线程竞争导致的性能下降。Netty 的串行化设计工作原理图如下图所示。 为了尽可能提升性能Netty 对消息的处理 采用了串行无锁化设计,在 I/O线程 内部进行串行操作避免多线程竞争导致的性能下降。Netty 的串行化设计工作原理图如下图所示。
![avatar](/images/Netty/Netty串行化设计工作原理.png) ![avatar](../../../images/Netty/Netty串行化设计工作原理.png)
Netty 的 NioEventLoop 读取到消息之后,直接调用 ChannelPipeline 的 fireChannelRead(Object msg),只要用户不主动切换线程,一直会由 NioEventLoop 调用到 用户的Handler期间不进行线程切换。这种串行化处理方式避免了多线程操作导致的锁的竞争从性能角度看是最优的。 Netty 的 NioEventLoop 读取到消息之后,直接调用 ChannelPipeline 的 fireChannelRead(Object msg),只要用户不主动切换线程,一直会由 NioEventLoop 调用到 用户的Handler期间不进行线程切换。这种串行化处理方式避免了多线程操作导致的锁的竞争从性能角度看是最优的。

@ -4,7 +4,7 @@ Linux 的内核将所有外部设备都看做一个文件来操作,对一个
#### 1、阻塞IO模型 #### 1、阻塞IO模型
在内核将数据准备好之前系统调用会一直等待所有的套接字Socket传来数据默认的是阻塞方式。 在内核将数据准备好之前系统调用会一直等待所有的套接字Socket传来数据默认的是阻塞方式。
![avatar](/images/Netty/阻塞IO模型.png) ![avatar](../../../images/Netty/阻塞IO模型.png)
Java 中的 socket.read()方法 最终会调用底层操作系统的 recvfrom方法OS 会判断来自网络的数据报是否准备好当数据报准备好了之后OS 就会将数据从内核空间拷贝到用户空间(因为我们的用户程序只能获取用户空间的内存,无法直接获取内核空间的内存)。拷贝完成之后 socket.read() 就会解除阻塞,并得到网络数据的结果。 Java 中的 socket.read()方法 最终会调用底层操作系统的 recvfrom方法OS 会判断来自网络的数据报是否准备好当数据报准备好了之后OS 就会将数据从内核空间拷贝到用户空间(因为我们的用户程序只能获取用户空间的内存,无法直接获取内核空间的内存)。拷贝完成之后 socket.read() 就会解除阻塞,并得到网络数据的结果。
@ -15,7 +15,7 @@ BIO中的阻塞就是阻塞在2个地方
在这2个时候我们的线程会一直被阻塞啥事情都不干。 在这2个时候我们的线程会一直被阻塞啥事情都不干。
#### 2、非阻塞IO模型 #### 2、非阻塞IO模型
![avatar](/images/Netty/非阻塞IO模型.png) ![avatar](../../../images/Netty/非阻塞IO模型.png)
每次应用程序询问内核是否有数据报准备好当有数据报准备好时就进行拷贝数据报的操作从内核拷贝到用户空间和拷贝完成返回的这段时间应用进程是阻塞的。但在没有数据报准备好时并不会阻塞程序内核直接返回未准备好的信号等待应用进程的下一次询问。但是轮寻对于CPU来说是较大的浪费一般只有在特定的场景下才使用。 每次应用程序询问内核是否有数据报准备好当有数据报准备好时就进行拷贝数据报的操作从内核拷贝到用户空间和拷贝完成返回的这段时间应用进程是阻塞的。但在没有数据报准备好时并不会阻塞程序内核直接返回未准备好的信号等待应用进程的下一次询问。但是轮寻对于CPU来说是较大的浪费一般只有在特定的场景下才使用。
@ -25,22 +25,22 @@ BIO中的阻塞就是阻塞在2个地方
#### 3、IO复用模型 #### 3、IO复用模型
Linux 提供 select/poll进程通过将一个或多个 fd 传递给 select 或 poll系统 调用,阻塞发生在 select/poll 操作上。select/poll 可以帮我们侦测多个 fd 是否处于就绪状态,它们顺序扫描 fd 是否就绪,但支持的 fd 数量有限因此它的使用也受到了一些制约。Linux 还提供了一个 epoll系统调用epoll 使用 基于事件驱动方式 代替 顺序扫描,因此性能更高,当有 fd 就绪时,立即回调函数 rollback。 Linux 提供 select/poll进程通过将一个或多个 fd 传递给 select 或 poll系统 调用,阻塞发生在 select/poll 操作上。select/poll 可以帮我们侦测多个 fd 是否处于就绪状态,它们顺序扫描 fd 是否就绪,但支持的 fd 数量有限因此它的使用也受到了一些制约。Linux 还提供了一个 epoll系统调用epoll 使用 基于事件驱动方式 代替 顺序扫描,因此性能更高,当有 fd 就绪时,立即回调函数 rollback。
![avatar](/images/Netty/IO复用模型.png) ![avatar](../../../images/Netty/IO复用模型.png)
#### 4、信号驱动IO模型 #### 4、信号驱动IO模型
首先开启套接口信号驱动IO功能并通过系统调用 sigaction 执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个 SIGIO信号通过信号回调通知应用程序调用 recvfrom 来读取数据,并通知主循环函数处理数据。 首先开启套接口信号驱动IO功能并通过系统调用 sigaction 执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个 SIGIO信号通过信号回调通知应用程序调用 recvfrom 来读取数据,并通知主循环函数处理数据。
![avatar](/images/Netty/信号驱动IO模型.png) ![avatar](../../../images/Netty/信号驱动IO模型.png)
#### 5、异步IO模型 #### 5、异步IO模型
告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是信号驱动IO 由内核通知我们何时可以开始一个 IO 操作异步IO模型 由内核通知我们 IO操作何时已经完成。 告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是信号驱动IO 由内核通知我们何时可以开始一个 IO 操作异步IO模型 由内核通知我们 IO操作何时已经完成。
![avatar](/images/Netty/异步IO模型.png) ![avatar](../../../images/Netty/异步IO模型.png)
从这五种 IO模型的结构 也可以看出阻塞程度阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO效率是由低到高的。 从这五种 IO模型的结构 也可以看出阻塞程度阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO效率是由低到高的。
最后我们看一下数据从客户端到服务器再由服务器返回结果数据的整体IO流程以便我们更好地理解上述的IO模型。 最后我们看一下数据从客户端到服务器再由服务器返回结果数据的整体IO流程以便我们更好地理解上述的IO模型。
![avatar](/images/Netty/数据在客户端及服务器之间的整体IO流程.png) ![avatar](../../../images/Netty/数据在客户端及服务器之间的整体IO流程.png)
## IO 多路复用技术 ## IO 多路复用技术
Java NIO 的核心类库中 多路复用器Selector 就是基于 epoll 的多路复用技术实现。 Java NIO 的核心类库中 多路复用器Selector 就是基于 epoll 的多路复用技术实现。

@ -7,7 +7,7 @@
通过下面的通信模型图可以发现,采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor线程 负责监听客户端的连接,它接收到客户 通过下面的通信模型图可以发现,采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor线程 负责监听客户端的连接,它接收到客户
端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 “一请求一应答” 通信模型。 端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 “一请求一应答” 通信模型。
![avatar](/images/Netty/BIO通信模型.png) ![avatar](../../../images/Netty/BIO通信模型.png)
该模型最大的问题就是缺乏弹性伸缩能力当客户端并发访问量增加后服务端的线程个数和客户端并发访问数呈1: 1的正比关系由于线程是 Java虚拟机 非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。 该模型最大的问题就是缺乏弹性伸缩能力当客户端并发访问量增加后服务端的线程个数和客户端并发访问数呈1: 1的正比关系由于线程是 Java虚拟机 非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
@ -19,7 +19,7 @@
### 伪异步IO模型图 ### 伪异步IO模型图
采用线程池和任务队列可以实现一种叫做 伪异步的IO通信框架其模型图下。当有新的客户端接入时将客户端的 Socket 封装成一个 Task对象 (该类实现了java.lang.Runnable接口)投递到后端的线程池中进行处理JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。 采用线程池和任务队列可以实现一种叫做 伪异步的IO通信框架其模型图下。当有新的客户端接入时将客户端的 Socket 封装成一个 Task对象 (该类实现了java.lang.Runnable接口)投递到后端的线程池中进行处理JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。
![avatar](/images/Netty/伪异步IO通信模型.png) ![avatar](../../../images/Netty/伪异步IO通信模型.png)
伪异步 IO通信框架 采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。但是由于它底层的通信依然采用同步阻塞模型,因此无法从根本上解决问题。 伪异步 IO通信框架 采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。但是由于它底层的通信依然采用同步阻塞模型,因此无法从根本上解决问题。
@ -94,14 +94,14 @@ Buffer对象 包含了一些要写入或者要读出的数据。在 NIO类库
缓冲区实质上是一个数组。通常它是一个字节数组ByteBuffer也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组缓冲区提供了对数据的结构化访问以及维护读写位置limit等信息。最常用的缓冲区是 ByteBuffer一个 ByteBuffer 提供了一组功能用于操作 byte数组。除了 ByteBuffer还有其他的一些缓冲区事实上每一种 Java基本类型除了 boolean都对应有一种与之对应的缓冲区CharBuffer、IntBuffer、DoubleBuffer 等等。Buffer组件中主要类的类图如下所示。 缓冲区实质上是一个数组。通常它是一个字节数组ByteBuffer也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组缓冲区提供了对数据的结构化访问以及维护读写位置limit等信息。最常用的缓冲区是 ByteBuffer一个 ByteBuffer 提供了一组功能用于操作 byte数组。除了 ByteBuffer还有其他的一些缓冲区事实上每一种 Java基本类型除了 boolean都对应有一种与之对应的缓冲区CharBuffer、IntBuffer、DoubleBuffer 等等。Buffer组件中主要类的类图如下所示。
![avatar](/images/Netty/Buffer组件类图.png) ![avatar](../../../images/Netty/Buffer组件类图.png)
除了ByteBuffer每一个 Buffer类 都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数 标准IO操作 都使用 ByteBuffer所以它在具有一般缓冲区的操作之外还提供了一些特有的操作以方便网络读写。 除了ByteBuffer每一个 Buffer类 都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数 标准IO操作 都使用 ByteBuffer所以它在具有一般缓冲区的操作之外还提供了一些特有的操作以方便网络读写。
**2、通道Channel** **2、通道Channel**
Channel 是一个通道,它就像自来水管一样,网络数据通过 Channel 读取和写入。通道与流的不同之处在于通道是双向的,可以用于读、写,或者二者同时进行;流是单向的,要么是 InputStream要么是 OutputStream。因为 Channel 是全双工的,所以它可以比流更好地映射底层操作系统的 API。特别是在 UNIX网络编程模型 中底层操作系统的通道都是全双工的同时支持读写操作。Channel组件中 主要类的类图如下所示,从中我们可以看到最常用的 ServerSocketChannel 和 SocketChannel。 Channel 是一个通道,它就像自来水管一样,网络数据通过 Channel 读取和写入。通道与流的不同之处在于通道是双向的,可以用于读、写,或者二者同时进行;流是单向的,要么是 InputStream要么是 OutputStream。因为 Channel 是全双工的,所以它可以比流更好地映射底层操作系统的 API。特别是在 UNIX网络编程模型 中底层操作系统的通道都是全双工的同时支持读写操作。Channel组件中 主要类的类图如下所示,从中我们可以看到最常用的 ServerSocketChannel 和 SocketChannel。
![avatar](/images/Netty/Channel组件类图.png) ![avatar](../../../images/Netty/Channel组件类图.png)
**3、多路复用器Selector** **3、多路复用器Selector**
多路复用器Selector 是 Java NIO编程 的基础,熟练地掌握 Selector 对于 NIO编程 至关重要。多路复用器提供选择已经就绪的任务的能力。简单来讲Selector会不断地轮询 “注册在其上的Channel”如果某个 Channel 上面发生读或者写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以获取 “就绪 Channel 的集合”,进行后续的 IO操作。 多路复用器Selector 是 Java NIO编程 的基础,熟练地掌握 Selector 对于 NIO编程 至关重要。多路复用器提供选择已经就绪的任务的能力。简单来讲Selector会不断地轮询 “注册在其上的Channel”如果某个 Channel 上面发生读或者写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以获取 “就绪 Channel 的集合”,进行后续的 IO操作。
@ -110,7 +110,7 @@ Channel 是一个通道,它就像自来水管一样,网络数据通过 Chann
### NIO服务端序列图 ### NIO服务端序列图
![avatar](/images/Netty/NIO服务端序列图.png) ![avatar](../../../images/Netty/NIO服务端序列图.png)
下面,我们看一下 NIO服务端 的主要创建过程。 下面,我们看一下 NIO服务端 的主要创建过程。
@ -191,7 +191,7 @@ Channel 是一个通道,它就像自来水管一样,网络数据通过 Chann
### NIO 客户端序列图 ### NIO 客户端序列图
![avatar](/images/Netty/NIO客户端序列图.png) ![avatar](../../../images/Netty/NIO客户端序列图.png)
1、打开 SocketChannel绑定客户端本地地址 (可选,默认系统会随机分配一个可用的本地地址),示例代码如下。 1、打开 SocketChannel绑定客户端本地地址 (可选,默认系统会随机分配一个可用的本地地址),示例代码如下。
```java ```java
@ -295,7 +295,7 @@ NIO2.0 的异步套接字通道是真正的 异步非阻塞IO对应于 UNIX
## 四种IO编程模型的对比 ## 四种IO编程模型的对比
对比之前,这里再澄清一下 “伪异步IO” 的概念。伪异步IO 的概念完全来源于实践,并没有官方说法。在 JDK NIO编程 没有流行之前,为了解决 Tomcat 通信线程同步IO 导致业务线程被挂住的问题,大家想到了一个办法,在通信线程和业务线程之间做个缓冲区,这个缓冲区用于隔离 IO线程 和业务线程间的直接访问,这样业务线程就不会被 IO线程 阻塞。而对于后端的业务侧来说,将消息或者 Task 放到线程池后就返回了,它不再直接访问 IO线程 或者进行 IO读写这样也就不会被同步阻塞。 对比之前,这里再澄清一下 “伪异步IO” 的概念。伪异步IO 的概念完全来源于实践,并没有官方说法。在 JDK NIO编程 没有流行之前,为了解决 Tomcat 通信线程同步IO 导致业务线程被挂住的问题,大家想到了一个办法,在通信线程和业务线程之间做个缓冲区,这个缓冲区用于隔离 IO线程 和业务线程间的直接访问,这样业务线程就不会被 IO线程 阻塞。而对于后端的业务侧来说,将消息或者 Task 放到线程池后就返回了,它不再直接访问 IO线程 或者进行 IO读写这样也就不会被同步阻塞。
![avatar](/images/Netty/四种IO模型的功能特性对比图.png) ![avatar](../../../images/Netty/四种IO模型的功能特性对比图.png)
## 选择 Netty 开发项目的理由 ## 选择 Netty 开发项目的理由
从可维护性角度看,由于 NIO 采用了异步非阻塞编程模型,而且是一个 IO线程 处理多条链路,它的调试和跟踪非常麻烦,特别是生产环境中的问题,我们无法进行有效的调试和跟踪,往往只能靠一些日志来辅助分析,定位难度很大。 从可维护性角度看,由于 NIO 采用了异步非阻塞编程模型,而且是一个 IO线程 处理多条链路,它的调试和跟踪非常麻烦,特别是生产环境中的问题,我们无法进行有效的调试和跟踪,往往只能靠一些日志来辅助分析,定位难度很大。

@ -15,7 +15,7 @@ Java中将输入输出抽象称为流就好像水管将两个容器连接
##### 2.1 阻塞IOBlocking I/O ##### 2.1 阻塞IOBlocking I/O
在内核将数据准备好之前系统调用会一直等待所有的套接字Socket默认的是阻塞方式。 在内核将数据准备好之前系统调用会一直等待所有的套接字Socket默认的是阻塞方式。
![avatar](/images/Netty/阻塞IO模型.png) ![avatar](../../../images/Netty/阻塞IO模型.png)
Java中的socket.read()会调用native read()而Java中的native方法会调用操作系统底层的dll而dll是C/C++编写的图中的recvfrom其实是C语言socket编程中的一个方法。所以其实我们在Java中调用socket.read()最后也会调用到图中的recvfrom方法。 Java中的socket.read()会调用native read()而Java中的native方法会调用操作系统底层的dll而dll是C/C++编写的图中的recvfrom其实是C语言socket编程中的一个方法。所以其实我们在Java中调用socket.read()最后也会调用到图中的recvfrom方法。
@ -28,7 +28,7 @@ BIO中的阻塞就是阻塞在2个地方
在这2个时候我们的BIO程序就是占着茅坑不拉屎啥事情都不干。 在这2个时候我们的BIO程序就是占着茅坑不拉屎啥事情都不干。
##### 2.2 非阻塞IONoblocking I/O ##### 2.2 非阻塞IONoblocking I/O
![avatar](/images/Netty/非阻塞IO模型.png) ![avatar](../../../images/Netty/非阻塞IO模型.png)
每次应用进程询问内核是否有数据报准备好当有数据报准备好时就进行拷贝数据报的操作从内核拷贝到用户空间和拷贝完成返回的这段时间应用进程是阻塞的。但在没有数据报准备好时并不会阻塞程序内核直接返回未准备就绪的信号等待应用进程的下一个轮寻。但是轮寻对于CPU来说是较大的浪费一般只有在特定的场景下才使用。 每次应用进程询问内核是否有数据报准备好当有数据报准备好时就进行拷贝数据报的操作从内核拷贝到用户空间和拷贝完成返回的这段时间应用进程是阻塞的。但在没有数据报准备好时并不会阻塞程序内核直接返回未准备就绪的信号等待应用进程的下一个轮寻。但是轮寻对于CPU来说是较大的浪费一般只有在特定的场景下才使用。
@ -48,7 +48,7 @@ serverSocketChannel.configureBlocking(false);
**BIO 不会在recvfrom询问数据是否准备好时阻塞但还是会在将数据从内核空间拷贝到用户空间时阻塞。一定要注意这个地方Non-Blocking还是会阻塞的。** **BIO 不会在recvfrom询问数据是否准备好时阻塞但还是会在将数据从内核空间拷贝到用户空间时阻塞。一定要注意这个地方Non-Blocking还是会阻塞的。**
##### 2.3 IO多路复用I/O Multiplexing ##### 2.3 IO多路复用I/O Multiplexing
![avatar](/images/Netty/IO复用模型.png) ![avatar](../../../images/Netty/IO复用模型.png)
传统情况下client与server通信需要一个3个socket(客户端的socket服务端的serversocket服务端中用来和客户端通信的socket)而在IO多路复用中客户端与服务端通信需要的不是socket而是3个channel通过channel可以完成与socket同样的操作channel的底层还是使用的socket进行通信但是多个channel只对应一个socket(可能不只是一个但是socket的数量一定少于channel数量)这样仅仅通过少量的socket就可以完成更多的连接提高了client容量。 传统情况下client与server通信需要一个3个socket(客户端的socket服务端的serversocket服务端中用来和客户端通信的socket)而在IO多路复用中客户端与服务端通信需要的不是socket而是3个channel通过channel可以完成与socket同样的操作channel的底层还是使用的socket进行通信但是多个channel只对应一个socket(可能不只是一个但是socket的数量一定少于channel数量)这样仅仅通过少量的socket就可以完成更多的连接提高了client容量。
@ -60,12 +60,12 @@ Mackqueue
**selectorepollkqueue都属于Reactor IO设计。** **selectorepollkqueue都属于Reactor IO设计。**
##### 2.4 信号驱动Signal driven IO ##### 2.4 信号驱动Signal driven IO
![avatar](/images/Netty/信号驱动IO模型.png) ![avatar](../../../images/Netty/信号驱动IO模型.png)
信号驱动IO模型应用进程告诉内核当数据报准备好的时候给我发送一个信号对SIGIO信号进行捕捉并且调用我的信号处理函数来获取数据报。 信号驱动IO模型应用进程告诉内核当数据报准备好的时候给我发送一个信号对SIGIO信号进行捕捉并且调用我的信号处理函数来获取数据报。
##### 2.5 异步IOAsynchronous I/O ##### 2.5 异步IOAsynchronous I/O
![avatar](/images/Netty/异步IO模型.png) ![avatar](../../../images/Netty/异步IO模型.png)
Asynchronous IO调用中是真正的无阻塞其他IO model中多少会有点阻塞。程序发起read操作之后立刻就可以开始去做其它的事。而在内核角度当它受到一个asynchronous read之后首先它会立刻返回所以不会对用户进程产生任何block。然后kernel会等待数据准备完成然后将数据拷贝到用户内存当这一切都完成之后kernel会给用户进程发送一个signal告诉它read操作完成了。 Asynchronous IO调用中是真正的无阻塞其他IO model中多少会有点阻塞。程序发起read操作之后立刻就可以开始去做其它的事。而在内核角度当它受到一个asynchronous read之后首先它会立刻返回所以不会对用户进程产生任何block。然后kernel会等待数据准备完成然后将数据拷贝到用户内存当这一切都完成之后kernel会给用户进程发送一个signal告诉它read操作完成了。

@ -4,7 +4,7 @@
### TCP粘包/拆包问题说明 ### TCP粘包/拆包问题说明
TCP 是个 “流” 协议所谓流就是没有界限的一串数据。TCP底层 并不了解上层(如 HTTP协议业务数据的具体含义它会根据 TCP缓冲区 的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的 TCP粘包和拆包问题。我们可以通过下面的示例图对 TCP粘包和拆包问题 进行说明。 TCP 是个 “流” 协议所谓流就是没有界限的一串数据。TCP底层 并不了解上层(如 HTTP协议业务数据的具体含义它会根据 TCP缓冲区 的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的 TCP粘包和拆包问题。我们可以通过下面的示例图对 TCP粘包和拆包问题 进行说明。
![avatar](/images/Netty/TCP粘包拆包问题.png) ![avatar](../../../images/Netty/TCP粘包拆包问题.png)
假设客户端依次发送了两个数据包 DI 和 D2 给服务端由于服务端一次读取到的字节数是不确定的故可能存在以下4种情况。 假设客户端依次发送了两个数据包 DI 和 D2 给服务端由于服务端一次读取到的字节数是不确定的故可能存在以下4种情况。
1. 服务端分两次读取到了两个独立的数据包,分别是 D1 和 D2没有粘包和拆包 1. 服务端分两次读取到了两个独立的数据包,分别是 D1 和 D2没有粘包和拆包

@ -5,7 +5,7 @@ Netty 为了向使用者屏蔽 NIO通信 的底层细节,在和用户交互的
### 基于 Netty 创建客户端 时序图 ### 基于 Netty 创建客户端 时序图
![avatar](/images/Netty/基于Netty创建客户端时序图.png) ![avatar](../../../images/Netty/基于Netty创建客户端时序图.png)
### Netty 创建客户端 流程分析 ### Netty 创建客户端 流程分析
1. 用户线程创建 Bootstrap实例通过 API 设置客户端相关的参数,异步发起客户端连接; 1. 用户线程创建 Bootstrap实例通过 API 设置客户端相关的参数,异步发起客户端连接;

@ -3,7 +3,7 @@
### Netty 服务端创建时序图 ### Netty 服务端创建时序图
![avatar](/images/Netty/Netty服务端创建时序图.png) ![avatar](../../../images/Netty/Netty服务端创建时序图.png)
下面我们对 Netty服务端创建 的关键步骤和原理进行详细解析。 下面我们对 Netty服务端创建 的关键步骤和原理进行详细解析。
@ -78,7 +78,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
8、**当轮询到 准备就绪的Channel 之后,就由 Reactor线程 NioEventLoop 执行 ChannelPipeline 的相应方法,最终调度并执行 ChannelHandler**,接口如下图所示。 8、**当轮询到 准备就绪的Channel 之后,就由 Reactor线程 NioEventLoop 执行 ChannelPipeline 的相应方法,最终调度并执行 ChannelHandler**,接口如下图所示。
![avatar](/images/Netty/ChannelPipeline的调度相关方法.png) ![avatar](../../../images/Netty/ChannelPipeline的调度相关方法.png)
9、**执行 Netty 中 系统的ChannelHandler 和 用户添加定制的ChannelHandler** 。ChannelPipeline 根据网络事件的类型,调度并执行 ChannelHandler相关代码如下。 9、**执行 Netty 中 系统的ChannelHandler 和 用户添加定制的ChannelHandler** 。ChannelPipeline 根据网络事件的类型,调度并执行 ChannelHandler相关代码如下。
```java ```java
@ -174,7 +174,7 @@ backlog 指定了内核为此套接口排队的最大连接个数,对于给定
TCP参数 设置完成后,用户可以为启动辅助类和其父类分别指定 Handler。两者 Handler 的用途不同:子类中的 Handler 是 NioServerSocketChannel 对应的 ChannelPipeline 的 Handler父类中的 Handler 是客户端新接入的连接 SocketChannel 对应的 ChannelPipeline 的 Handler。两者的区别可以通过下图来展示。 TCP参数 设置完成后,用户可以为启动辅助类和其父类分别指定 Handler。两者 Handler 的用途不同:子类中的 Handler 是 NioServerSocketChannel 对应的 ChannelPipeline 的 Handler父类中的 Handler 是客户端新接入的连接 SocketChannel 对应的 ChannelPipeline 的 Handler。两者的区别可以通过下图来展示。
![avatar](/images/Netty/ServerBootstrap的Handler模型.png) ![avatar](../../../images/Netty/ServerBootstrap的Handler模型.png)
本质区别就是ServerBootstrap 中的 Handler 是 NioServerSocketChannel 使用的所有连接该监听端口的客户端都会执行它父类AbstractBootstrap 中的 Handler 是个工厂类,它为每个新接入的客户端都创建一个新的 Handler。 本质区别就是ServerBootstrap 中的 Handler 是 NioServerSocketChannel 使用的所有连接该监听端口的客户端都会执行它父类AbstractBootstrap 中的 Handler 是个工厂类,它为每个新接入的客户端都创建一个新的 Handler。
@ -304,7 +304,7 @@ NioServerSocketChannel 创建成功后,对它进行初始化,初始化工作
} }
``` ```
到此Netty 服务端监听的相关资源已经初始化完毕,就剩下最后一步,注册 NioServerSocketChannel 到 Reactor线程 的多路复用器上,然后轮询客户端连接事件。在分析注册代码之前,我们先通过下图,看看目前 NioServerSocketChannel 的 ChannelPipeline 的组成。 到此Netty 服务端监听的相关资源已经初始化完毕,就剩下最后一步,注册 NioServerSocketChannel 到 Reactor线程 的多路复用器上,然后轮询客户端连接事件。在分析注册代码之前,我们先通过下图,看看目前 NioServerSocketChannel 的 ChannelPipeline 的组成。
![avatar](/images/Netty/NioServerSocketChannel的ChannelPipeline.png) ![avatar](../../../images/Netty/NioServerSocketChannel的ChannelPipeline.png)
最后,我们看下 NioServerSocketChannel 的注册。当 NioServerSocketChannel 初始化完成之后,需要将它注册到 Reactor线程 的多路复用器上监听新客户端的接入,代码如下。 最后,我们看下 NioServerSocketChannel 的注册。当 NioServerSocketChannel 初始化完成之后,需要将它注册到 Reactor线程 的多路复用器上监听新客户端的接入,代码如下。
```java ```java
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel { public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {

@ -9,7 +9,7 @@
``` ```
- 源码阅读目标找到了,那么怎么去找入口或者对这句话的标签解析方法呢?项目中使用搜索 - 源码阅读目标找到了,那么怎么去找入口或者对这句话的标签解析方法呢?项目中使用搜索
![image-20200115083744268](/images/spring/image-20200115083744268.png) ![image-20200115083744268](../../../images/spring/image-20200115083744268.png)
这样就找到了具体解析方法了 这样就找到了具体解析方法了
@ -17,7 +17,7 @@
- 类图 - 类图
![image-20200115084031725](/images/spring/image-20200115084031725.png) ![image-20200115084031725](../../../images/spring/image-20200115084031725.png)
```java ```java
@Override @Override
@Nullable @Nullable

@ -453,7 +453,7 @@ public void setDataSource(@Nullable DataSource dataSource) {
``` ```
![image-20200109150841916](/images/spring/image-20200109150841916.png) ![image-20200109150841916](../../../images/spring/image-20200109150841916.png)
这样就可以获取到了 这样就可以获取到了

@ -416,7 +416,7 @@ public class RMIClientSourceCode {
### RmiProxyFactoryBean ### RmiProxyFactoryBean
![image-20200225104850528](/images/spring/image-20200226082614312.png) ![image-20200225104850528](../../../images/spring/image-20200226082614312.png)
@ -671,7 +671,7 @@ protected Remote lookupStub() throws RemoteLookupFailureException {
- `RmiInvocationHandler`类图 - `RmiInvocationHandler`类图
![image-20200226082614312](/images/spring/image-20200226082614312.png) ![image-20200226082614312](../../../images/spring/image-20200226082614312.png)
@ -747,7 +747,7 @@ protected Remote lookupStub() throws RemoteLookupFailureException {
类图 类图
![image-20200226083247784](/images/spring/image-20200226083247784.png) ![image-20200226083247784](../../../images/spring/image-20200226083247784.png)
@ -787,7 +787,7 @@ public class DefaultRemoteInvocationExecutor implements RemoteInvocationExecutor
- `org.springframework.remoting.rmi.RmiServiceExporter#afterPropertiesSet`打上断点 - `org.springframework.remoting.rmi.RmiServiceExporter#afterPropertiesSet`打上断点
![image-20200226084056993](/images/spring/image-20200226084056993.png) ![image-20200226084056993](../../../images/spring/image-20200226084056993.png)
可以看到此时的数据字段和我们的xml配置中一致 可以看到此时的数据字段和我们的xml配置中一致
@ -795,85 +795,85 @@ public class DefaultRemoteInvocationExecutor implements RemoteInvocationExecutor
- `org.springframework.remoting.rmi.RmiServiceExporter#prepare`断点 - `org.springframework.remoting.rmi.RmiServiceExporter#prepare`断点
![image-20200226084200428](/images/spring/image-20200226084200428.png) ![image-20200226084200428](../../../images/spring/image-20200226084200428.png)
往下一直走 往下一直走
![image-20200226084400939](/images/spring/image-20200226084400939.png) ![image-20200226084400939](../../../images/spring/image-20200226084400939.png)
这一行是jdk的就不进去看了 这一行是jdk的就不进去看了
执行完成就创建出了 `Registry` 执行完成就创建出了 `Registry`
![image-20200226084514795](/images/spring/image-20200226084514795.png) ![image-20200226084514795](../../../images/spring/image-20200226084514795.png)
- `org.springframework.remoting.rmi.RmiBasedExporter#getObjectToExport` - `org.springframework.remoting.rmi.RmiBasedExporter#getObjectToExport`
直接看结果对象 直接看结果对象
![image-20200226084640683](/images/spring/image-20200226084640683.png) ![image-20200226084640683](../../../images/spring/image-20200226084640683.png)
- 执行bind - 执行bind
![image-20200226084923783](/images/spring/image-20200226084923783.png) ![image-20200226084923783](../../../images/spring/image-20200226084923783.png)
![image-20200226084914000](/images/spring/image-20200226084914000.png) ![image-20200226084914000](../../../images/spring/image-20200226084914000.png)
- 此时服务端信息已经成功记录并且启动 - 此时服务端信息已经成功记录并且启动
## 客户端debug ## 客户端debug
![image-20200226085433130](/images/spring/image-20200226085433130.png) ![image-20200226085433130](../../../images/spring/image-20200226085433130.png)
![image-20200226085440865](/images/spring/image-20200226085440865.png) ![image-20200226085440865](../../../images/spring/image-20200226085440865.png)
remote 对象 remote 对象
![image-20200226085727426](/images/spring/image-20200226085727426.png) ![image-20200226085727426](../../../images/spring/image-20200226085727426.png)
- 服务提供接口 - 服务提供接口
![image-20200226085839496](/images/spring/image-20200226085839496.png) ![image-20200226085839496](../../../images/spring/image-20200226085839496.png)
- serviceProxy - serviceProxy
![image-20200226090042946](/images/spring/image-20200226090042946.png) ![image-20200226090042946](../../../images/spring/image-20200226090042946.png)
- 方法调用 - 方法调用
- 使用的是AOP技术进行的AOP相关技术不在此处展开 - 使用的是AOP技术进行的AOP相关技术不在此处展开
![image-20200226090315865](/images/spring/image-20200226090315865.png) ![image-20200226090315865](../../../images/spring/image-20200226090315865.png)
stub 对象 stub 对象
![image-20200226090432052](/images/spring/image-20200226090432052.png) ![image-20200226090432052](../../../images/spring/image-20200226090432052.png)
![image-20200226090650154](/images/spring/image-20200226090650154.png) ![image-20200226090650154](../../../images/spring/image-20200226090650154.png)
- `invocation` - `invocation`
![image-20200226090719108](/images/spring/image-20200226090719108.png) ![image-20200226090719108](../../../images/spring/image-20200226090719108.png)
- `targetObject` - `targetObject`
![image-20200226090827849](/images/spring/image-20200226090827849.png) ![image-20200226090827849](../../../images/spring/image-20200226090827849.png)
- 反射执行`method`结束整个调用 - 反射执行`method`结束整个调用
![image-20200226090945418](/images/spring/image-20200226090945418.png) ![image-20200226090945418](../../../images/spring/image-20200226090945418.png)
此时得到结果RMI调用结束 此时得到结果RMI调用结束

@ -103,7 +103,7 @@ example.scannable.sub.BarComponent=org.springframework.stereotype.Component
![image-20200115105941265](/images/spring/image-20200115105941265.png) ![image-20200115105941265](../../../images/spring/image-20200115105941265.png)
- 该类给`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents`提供了帮助 - 该类给`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents`提供了帮助
```java ```java

@ -33,7 +33,7 @@ DispatchServlet 和 ContextLoaderListener 提供了在 Web容器 中对 Spring
## 2 IoC容器启动的基本过程 ## 2 IoC容器启动的基本过程
IoC容器 的启动过程就是建立上下文的过程,该上下文是与 ServletContext 相伴而生的,同时也是 IoC容器 在 Web应用环境 中的具体表现之一。由 ContextLoaderListener 启动的上下文为根上下文。在根上下文的基础上,还有一个与 Web MVC 相关的上下文用来保存控制器(DispatcherServlet)需要的 MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在 Web容器 中启动 Spring应用程序 时,首先建立根上下文,然后建立这个上下文体系,这个上下文体系的建立是由 ContextLoder 来完成的,其 UML时序图 如下图所示。 IoC容器 的启动过程就是建立上下文的过程,该上下文是与 ServletContext 相伴而生的,同时也是 IoC容器 在 Web应用环境 中的具体表现之一。由 ContextLoaderListener 启动的上下文为根上下文。在根上下文的基础上,还有一个与 Web MVC 相关的上下文用来保存控制器(DispatcherServlet)需要的 MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在 Web容器 中启动 Spring应用程序 时,首先建立根上下文,然后建立这个上下文体系,这个上下文体系的建立是由 ContextLoder 来完成的,其 UML时序图 如下图所示。
![avatar](/images/springMVC/Web容器启动spring应用程序过程图.png) ![avatar](../../../images/springMVC/Web容器启动spring应用程序过程图.png)
在 web.xml 中,已经配置了 ContextLoaderListener它是 Spring 提供的类,是为在 Web容器 中建立 IoC容器 服务的,它实现了 ServletContextListener接口这个接口是在 Servlet API 中定义的,提供了与 Servlet生命周期 结合的回调,比如上下文初始化 contextInitialized()方法 和 上下文销毁 contextDestroyed()方法。而在 Web容器 中,建立 WebApplicationContext 的过程,是在 contextInitialized()方法 中完成的。另外ContextLoaderListener 还继承了 ContextLoader具体的载入 IoC容器 的过程是由 ContextLoader 来完成的。 在 web.xml 中,已经配置了 ContextLoaderListener它是 Spring 提供的类,是为在 Web容器 中建立 IoC容器 服务的,它实现了 ServletContextListener接口这个接口是在 Servlet API 中定义的,提供了与 Servlet生命周期 结合的回调,比如上下文初始化 contextInitialized()方法 和 上下文销毁 contextDestroyed()方法。而在 Web容器 中,建立 WebApplicationContext 的过程,是在 contextInitialized()方法 中完成的。另外ContextLoaderListener 还继承了 ContextLoader具体的载入 IoC容器 的过程是由 ContextLoader 来完成的。
@ -43,7 +43,7 @@ IoC容器 的启动过程就是建立上下文的过程,该上下文是与 Ser
先从 Web容器 中的上下文入手,看看 Web环境 中的上下文设置有哪些特别之处,然后再到 ContextLoaderListener 中去了解整个容器启动的过程。为了方便在 Web环境 中使用 IoC容器 先从 Web容器 中的上下文入手,看看 Web环境 中的上下文设置有哪些特别之处,然后再到 ContextLoaderListener 中去了解整个容器启动的过程。为了方便在 Web环境 中使用 IoC容器
Spring 为 Web应用 提供了上下文的扩展接口 WebApplicationContext 来满足启动过程的需要,其继承关系如下图所示。 Spring 为 Web应用 提供了上下文的扩展接口 WebApplicationContext 来满足启动过程的需要,其继承关系如下图所示。
![avatar](/images/springMVC/WebApplicationContext接口的类继承关系.png) ![avatar](../../../images/springMVC/WebApplicationContext接口的类继承关系.png)
在这个类继承关系中,可以从熟悉的 XmlWebApplicationContext 入手来了解它的接口实现。在接口设计中,最后是通过 ApplicationContex接口 与 BeanFactory接口 对接的,而对于具体的功能实现,很多都是封装在其基类 AbstractRefreshableWebApplicationContext 中完成的。 在这个类继承关系中,可以从熟悉的 XmlWebApplicationContext 入手来了解它的接口实现。在接口设计中,最后是通过 ApplicationContex接口 与 BeanFactory接口 对接的,而对于具体的功能实现,很多都是封装在其基类 AbstractRefreshableWebApplicationContext 中完成的。

@ -127,9 +127,9 @@ public class JSONController {
信息截图: 信息截图:
![image-20200123085741347](/images/springMVC/clazz/image-20200123085741347.png) ![image-20200123085741347](../../../images/springMVC/clazz/image-20200123085741347.png)
![image-20200123085756168](/images/springMVC/clazz/image-20200123085756168.png) ![image-20200123085756168](../../../images/springMVC/clazz/image-20200123085756168.png)
@ -180,7 +180,7 @@ public class JSONController {
最终解析结果 最终解析结果
![image-20200123085946476](/images/springMVC/clazz/image-20200123085946476.png) ![image-20200123085946476](../../../images/springMVC/clazz/image-20200123085946476.png)
@ -263,7 +263,7 @@ public class JSONController {
#### 类图 #### 类图
![image-20200123090442409](/images/springMVC/clazz/image-20200123090442409.png) ![image-20200123090442409](../../../images/springMVC/clazz/image-20200123090442409.png)
#### 解析 #### 解析
@ -329,7 +329,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
- 属性截图 - 属性截图
![image-20200123090851644](/images/springMVC/clazz/image-20200123090851644.png) ![image-20200123090851644](../../../images/springMVC/clazz/image-20200123090851644.png)
- 可以看出这个是我们的第一个跨域配置的信息 - 可以看出这个是我们的第一个跨域配置的信息
@ -363,7 +363,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
``` ```
- ![image-20200123091445694](/images/springMVC/clazz/image-20200123091445694.png) - ![image-20200123091445694](../../../images/springMVC/clazz/image-20200123091445694.png)
@ -555,7 +555,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
- 经过跨域拦截器 **`CorsInterceptor`**之后会调用 - 经过跨域拦截器 **`CorsInterceptor`**之后会调用
![image-20200123093733129](/images/springMVC/clazz/image-20200123093733129.png) ![image-20200123093733129](../../../images/springMVC/clazz/image-20200123093733129.png)
@ -612,4 +612,4 @@ Origin: localhost
![image-20200123093032179](/images/springMVC/clazz/image-20200123093032179.png) ![image-20200123093032179](../../../images/springMVC/clazz/image-20200123093032179.png)

@ -8,13 +8,13 @@
为了解这个过程,可以从 DispatcherServlet 的父类 FrameworkServlet 的代码入手,去探寻 DispatcherServlet 的启动过程,它同时也是 SpringMVC 的启动过程。ApplicationContext 的创建过程和 ContextLoader 创建根上下文的过程有许多类似的地方。下面来看一下这个 DispatcherServlet类 的继承关系。 为了解这个过程,可以从 DispatcherServlet 的父类 FrameworkServlet 的代码入手,去探寻 DispatcherServlet 的启动过程,它同时也是 SpringMVC 的启动过程。ApplicationContext 的创建过程和 ContextLoader 创建根上下文的过程有许多类似的地方。下面来看一下这个 DispatcherServlet类 的继承关系。
![avatar](/images/springMVC/DispatcherServlet的继承关系.png) ![avatar](../../../images/springMVC/DispatcherServlet的继承关系.png)
DispatcherServlet 通过继承 FrameworkServlet 和 HttpServletBean 而继承了 HttpServlet通过使用Servlet API 来对 HTTP请求 进行响应,成为 SpringMVC 的前端处理器,同时成为 MVC模块 与 Web容器 集成的处理前端。 DispatcherServlet 通过继承 FrameworkServlet 和 HttpServletBean 而继承了 HttpServlet通过使用Servlet API 来对 HTTP请求 进行响应,成为 SpringMVC 的前端处理器,同时成为 MVC模块 与 Web容器 集成的处理前端。
DispatcherServlet 的工作大致可以分为两个部分:一个是初始化部分,由 initServletBean()方法 启动,通过 initWebApplicationContext()方法 最终调用 DispatcherServlet 的 initStrategies()方法在这个方法里DispatcherServlet 对 MVC模块 的其他部分进行了初始化,比如 handlerMapping、ViewResolver 等;另一个是对 HTTP请求 进行响应,作为一个 ServletWeb容器 会调用 Servlet 的doGet() 和 doPost()方法,在经过 FrameworkServlet 的 processRequest() 简单处理后,会调用 DispatcherServlet 的 doService()方法,在这个方法调用中封装了 doDispatch(),这个 doDispatch() 是 Dispatcher 实现 MVC模式 的主要部分,下图为 DispatcherServlet 的处理过程时序图。 DispatcherServlet 的工作大致可以分为两个部分:一个是初始化部分,由 initServletBean()方法 启动,通过 initWebApplicationContext()方法 最终调用 DispatcherServlet 的 initStrategies()方法在这个方法里DispatcherServlet 对 MVC模块 的其他部分进行了初始化,比如 handlerMapping、ViewResolver 等;另一个是对 HTTP请求 进行响应,作为一个 ServletWeb容器 会调用 Servlet 的doGet() 和 doPost()方法,在经过 FrameworkServlet 的 processRequest() 简单处理后,会调用 DispatcherServlet 的 doService()方法,在这个方法调用中封装了 doDispatch(),这个 doDispatch() 是 Dispatcher 实现 MVC模式 的主要部分,下图为 DispatcherServlet 的处理过程时序图。
![avatar](/images/springMVC/DispatcherServlet的处理过程.png) ![avatar](../../../images/springMVC/DispatcherServlet的处理过程.png)
## 3 DispatcherServlet的启动和初始化 ## 3 DispatcherServlet的启动和初始化
前面大致描述了 SpringMVC 的工作流程,下面看一下 DispatcherServlet 的启动和初始化的代码设计及实现。 前面大致描述了 SpringMVC 的工作流程,下面看一下 DispatcherServlet 的启动和初始化的代码设计及实现。
@ -376,7 +376,7 @@ HandlerMappings 完成对 MVC 中 Controller 的定义和配置,只不过在 W
在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMappings 被放在一个 List 中并被排序,存储着 HTTP请求 对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL请求 到 Controller 的映射,而 SpringMVC 提供了一系列的 HandlerMapping 实现。 在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMappings 被放在一个 List 中并被排序,存储着 HTTP请求 对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL请求 到 Controller 的映射,而 SpringMVC 提供了一系列的 HandlerMapping 实现。
![avatar](/images/springMVC/HandlerMapping组件.png) ![avatar](../../../images/springMVC/HandlerMapping组件.png)
以 SimpleUrlHandlerMapping 为例来分析 HandlerMapping 的设计与实现。在 SimpleUrlHandlerMapping 中,定义了一个 Map 来持有一系列的映射关系。通过这些在 HandlerMapping 中定义的映射关系,即这些 URL请求 和控制器的对应关系,使 SpringMVC 以 SimpleUrlHandlerMapping 为例来分析 HandlerMapping 的设计与实现。在 SimpleUrlHandlerMapping 中,定义了一个 Map 来持有一系列的映射关系。通过这些在 HandlerMapping 中定义的映射关系,即这些 URL请求 和控制器的对应关系,使 SpringMVC
应用 可以根据 HTTP请求 确定一个对应的 Controller。具体来说这些映射关系是通过 HandlerMapping接口 来封装的,在 HandlerMapping接口 中定义了一个 getHandler()方法,通过这个方法,可以获得与 HTTP请求 对应的 HandlerExecutionChain在这个 HandlerExecutionChain 中,封装了具体的 Controller对象。 应用 可以根据 HTTP请求 确定一个对应的 Controller。具体来说这些映射关系是通过 HandlerMapping接口 来封装的,在 HandlerMapping接口 中定义了一个 getHandler()方法,通过这个方法,可以获得与 HTTP请求 对应的 HandlerExecutionChain在这个 HandlerExecutionChain 中,封装了具体的 Controller对象。
@ -496,7 +496,7 @@ public class HandlerExecutionChain {
``` ```
HandlerExecutionChain 中定义的 Handler 和 HandlerInterceptor[]属性 需要在定义 HandlerMapping 时配置好,例如对具体的 SimpleURLHandlerMapping要做的就是根据 URL映射 的方式,注册 Handler 和 HandlerInterceptor[],从而维护一个反映这种映射关系的 handlerMap。当需要匹配 HTTP请求 时,需要查询这个 handlerMap 中的信息来得到对应的 HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对 Bean 进行依赖注入时发生,它实际上是通过一个 Bean 的 postProcessor() 来完成的。以 SimpleHandlerMapping 为例,需要注意的是,这里用到了对容器的回调,只有 SimpleHandlerMapping 是 ApplicationContextAware 的子类才能启动这个注册过程。这个注册过程完成的是反映 URL 和 Controller 之间映射关系的 handlerMap 的建立。 HandlerExecutionChain 中定义的 Handler 和 HandlerInterceptor[]属性 需要在定义 HandlerMapping 时配置好,例如对具体的 SimpleURLHandlerMapping要做的就是根据 URL映射 的方式,注册 Handler 和 HandlerInterceptor[],从而维护一个反映这种映射关系的 handlerMap。当需要匹配 HTTP请求 时,需要查询这个 handlerMap 中的信息来得到对应的 HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对 Bean 进行依赖注入时发生,它实际上是通过一个 Bean 的 postProcessor() 来完成的。以 SimpleHandlerMapping 为例,需要注意的是,这里用到了对容器的回调,只有 SimpleHandlerMapping 是 ApplicationContextAware 的子类才能启动这个注册过程。这个注册过程完成的是反映 URL 和 Controller 之间映射关系的 handlerMap 的建立。
![avatar](/images/springMVC/SimpleUrlHandlerMapping的继承关系.png) ![avatar](../../../images/springMVC/SimpleUrlHandlerMapping的继承关系.png)
```java ```java
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

@ -6,7 +6,7 @@ JavaEE应用 中的事务处理是一个重要并且涉及范围很广的领域
## 1 Spring事务处理 的设计概览 ## 1 Spring事务处理 的设计概览
Spring事务处理模块 的类层次结构如下图所示。 Spring事务处理模块 的类层次结构如下图所示。
![avatar](/images/springTransaction/Spring事务处理模块类层次结构.png) ![avatar](../../../images/springTransaction/Spring事务处理模块类层次结构.png)
从上图可以看到Spring事务处理模块 是通过 AOP功能 来实现声明式事务处理的,比如事务属性的配置和读取,事务对象的抽象等。因此,在 Spring事务处理 中,可以通过设计一个 TransactionProxyFactoryBean 来使用 AOP功能通过这个 TransactionProxyFactoryBean 可以生成 Proxy代理对象在这个代理对象中通过 TransactionInterceptor 来完成对代理方法的拦截,正是这些 AOP 的拦截功能,将事务处理的功能编织进来。 从上图可以看到Spring事务处理模块 是通过 AOP功能 来实现声明式事务处理的,比如事务属性的配置和读取,事务对象的抽象等。因此,在 Spring事务处理 中,可以通过设计一个 TransactionProxyFactoryBean 来使用 AOP功能通过这个 TransactionProxyFactoryBean 可以生成 Proxy代理对象在这个代理对象中通过 TransactionInterceptor 来完成对代理方法的拦截,正是这些 AOP 的拦截功能,将事务处理的功能编织进来。

@ -3,7 +3,7 @@
可以看到,在 PlatformTransactionManager组件 的设计中 ,通过 PlatformTransactionManager接口 设计了一系列与事务处理息息相关的接口方法,如 getTransaction()、commit()、rollback() 这些和事务处理相关的统一接口。对于这些接口的实现,很大一部分是由 AbstractTransactionManager抽象类 来完成的,这个类中的 doGetTransaction()、doCommit() 等方法和 PlatformTransactionManager 的方法对应,实现的是事务处理中相对通用的部分。在这个 AbstractPlatformManager 下,为具体的数据源配置了不同的事务处理器,以处理不同数据源的事务处理,从而形成了一个从抽象到具体的事务处理中间平台设计,使应用通过声明式事务处理,即开即用事务处理服务,隔离那些与特定的数据源相关的具体实现。 可以看到,在 PlatformTransactionManager组件 的设计中 ,通过 PlatformTransactionManager接口 设计了一系列与事务处理息息相关的接口方法,如 getTransaction()、commit()、rollback() 这些和事务处理相关的统一接口。对于这些接口的实现,很大一部分是由 AbstractTransactionManager抽象类 来完成的,这个类中的 doGetTransaction()、doCommit() 等方法和 PlatformTransactionManager 的方法对应,实现的是事务处理中相对通用的部分。在这个 AbstractPlatformManager 下,为具体的数据源配置了不同的事务处理器,以处理不同数据源的事务处理,从而形成了一个从抽象到具体的事务处理中间平台设计,使应用通过声明式事务处理,即开即用事务处理服务,隔离那些与特定的数据源相关的具体实现。
![avatar](/images/springTransaction/PlatformTransactionManager组件的设计.png) ![avatar](../../../images/springTransaction/PlatformTransactionManager组件的设计.png)
## 2 DataSourceTransactionManager的实现 ## 2 DataSourceTransactionManager的实现
我们先看一下 DataSourceTransactionManager在这个事务管理器中它的实现直接与事务处理的底层实现相关。在事务开始的时候会调用 doBegin()方法,首先会得到相对应的 Connection然后可以根据事务设置的需要对 Connection 的相关属性进行配置,比如将 Connection 的 autoCommit功能 关闭,并对像 TimeoutInSeconds 这样的事务处理参数进行设置,最后通过 TransactionSynchronizationManager 来对资源进行绑定。 我们先看一下 DataSourceTransactionManager在这个事务管理器中它的实现直接与事务处理的底层实现相关。在事务开始的时候会调用 doBegin()方法,首先会得到相对应的 Connection然后可以根据事务设置的需要对 Connection 的相关属性进行配置,比如将 Connection 的 autoCommit功能 关闭,并对像 TimeoutInSeconds 这样的事务处理参数进行设置,最后通过 TransactionSynchronizationManager 来对资源进行绑定。
@ -12,7 +12,7 @@
上面介绍了使用 DataSourceTransactionManager 实现事务创建、提交和回滚的过程,基本上与单独使用 Connection 实现事务处理是一样的,也是通过设置 autoCommit属性调用 Connection 的 commit() 和 rollback()方法 来完成的。而我们在声明式事务处理中看到的那些事务处理属性,并不在 DataSourceTransactionManager 中完成,这和我们在前面分析中看到的是一致的。 上面介绍了使用 DataSourceTransactionManager 实现事务创建、提交和回滚的过程,基本上与单独使用 Connection 实现事务处理是一样的,也是通过设置 autoCommit属性调用 Connection 的 commit() 和 rollback()方法 来完成的。而我们在声明式事务处理中看到的那些事务处理属性,并不在 DataSourceTransactionManager 中完成,这和我们在前面分析中看到的是一致的。
![avatar](/images/springTransaction/实现DataSourceTransactionManager的时序图.png) ![avatar](../../../images/springTransaction/实现DataSourceTransactionManager的时序图.png)
```java ```java
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager public class DataSourceTransactionManager extends AbstractPlatformTransactionManager

@ -71,7 +71,7 @@ public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBe
``` ```
以上代码完成了 AOP配置对于用户来说一个值得关心的问题是Spring 的 TransactionInterceptor配置 是在什么时候被启动并成为 Advisor通知器 的一部分的呢?从对 createMainInterceptor()方法 的调用分析中可以看到,这个 createMainInterceptor()方法 在 IoC容器 完成 Bean的依赖注入时通过 initializeBean()方法 被调用,具体的调用过程如下图所示。 以上代码完成了 AOP配置对于用户来说一个值得关心的问题是Spring 的 TransactionInterceptor配置 是在什么时候被启动并成为 Advisor通知器 的一部分的呢?从对 createMainInterceptor()方法 的调用分析中可以看到,这个 createMainInterceptor()方法 在 IoC容器 完成 Bean的依赖注入时通过 initializeBean()方法 被调用,具体的调用过程如下图所示。
![avatar](/images/springTransaction/createMainInterceptor()方法的调用链.png) ![avatar](../../../images/springTransaction/createMainInterceptor()方法的调用链.png)
在 TransactionProxyFactoryBean 的父类 AbstractSingletonProxyFactoryBean 中的 afterPropertiesSet()方法,是 Spring事务处理 完成 AOP配置 的地方,在建立 TransactionProxyFactoryBean 的事务处理拦截器的时候,首先需要对 ProxyFactoryBean 的 目标Bean 设置进行检查,如果这个 目标Bean 的设置是正确的,就会创建一个 ProxyFactory对象从而实现 AOP 的使用。在 afterPropertiesSet() 的方法实现中,可以看到为 ProxyFactory 生成代理对象、配置通知器、设置代理接口方法等。 在 TransactionProxyFactoryBean 的父类 AbstractSingletonProxyFactoryBean 中的 afterPropertiesSet()方法,是 Spring事务处理 完成 AOP配置 的地方,在建立 TransactionProxyFactoryBean 的事务处理拦截器的时候,首先需要对 ProxyFactoryBean 的 目标Bean 设置进行检查,如果这个 目标Bean 的设置是正确的,就会创建一个 ProxyFactory对象从而实现 AOP 的使用。在 afterPropertiesSet() 的方法实现中,可以看到为 ProxyFactory 生成代理对象、配置通知器、设置代理接口方法等。
```java ```java

@ -50,11 +50,11 @@
``` ```
- method - method
![image-20200116085344737](/images/spring/image-20200116085344737.png) ![image-20200116085344737](../../../images/spring/image-20200116085344737.png)
- annotationType - annotationType
![image-20200116085423073](/images/spring/image-20200116085423073.png) ![image-20200116085423073](../../../images/spring/image-20200116085423073.png)
```java ```java
@Nullable @Nullable
@ -229,9 +229,9 @@
处理结果 处理结果
![image-20200116085726577](/images/spring/image-20200116085726577.png) ![image-20200116085726577](../../../images/spring/image-20200116085726577.png)
![image-20200116085737632](/images/spring/image-20200116085737632.png) ![image-20200116085737632](../../../images/spring/image-20200116085737632.png)
处理结果和Order定义相同 处理结果和Order定义相同
@ -257,7 +257,7 @@ public @interface Order {
最终返回 最终返回
![image-20200116085927359](/images/spring/image-20200116085927359.png) ![image-20200116085927359](../../../images/spring/image-20200116085927359.png)
@ -412,7 +412,7 @@ public @interface Order {
![image-20200116092259944](/images/spring/image-20200116092259944.png) ![image-20200116092259944](../../../images/spring/image-20200116092259944.png)
- `synthesizeAnnotation`方法就不再重复一遍了可以看上文 - `synthesizeAnnotation`方法就不再重复一遍了可以看上文

@ -112,7 +112,7 @@ public class ListenerSourceCode {
``` ```
![image-20200119163638222](/images/spring/image-20200119163638222.png) ![image-20200119163638222](../../../images/spring/image-20200119163638222.png)
@ -190,7 +190,7 @@ protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
- 执行监听方法 - 执行监听方法
![image-20200119164149650](/images/spring/image-20200119164149650.png) ![image-20200119164149650](../../../images/spring/image-20200119164149650.png)
@ -241,6 +241,6 @@ protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
![image-20200119164402137](/images/spring/image-20200119164402137.png) ![image-20200119164402137](../../../images/spring/image-20200119164402137.png)
![image-20200119164410301](/images/spring/image-20200119164410301.png) ![image-20200119164410301](../../../images/spring/image-20200119164410301.png)

@ -251,9 +251,9 @@ public class BeanFactoryPostProcessorSourceCode {
![image-20200119085346675](/images/spring/image-20200119085346675.png) ![image-20200119085346675](../../../images/spring/image-20200119085346675.png)
![image-20200119085655734](/images/spring/image-20200119085655734.png) ![image-20200119085655734](../../../images/spring/image-20200119085655734.png)
@ -374,15 +374,15 @@ public class DemoInstantiationAwareBeanPostProcessor implements InstantiationAwa
- 按照笔者的注释,可以知道`DemoInstantiationAwareBeanPostProcessor` 这个类是一个无序Bean - 按照笔者的注释,可以知道`DemoInstantiationAwareBeanPostProcessor` 这个类是一个无序Bean
![image-20200119101026726](/images/spring/image-20200119101026726.png) ![image-20200119101026726](../../../images/spring/image-20200119101026726.png)
![image-20200119101017989](/images/spring/image-20200119101017989.png) ![image-20200119101017989](../../../images/spring/image-20200119101017989.png)
- 注册方法信息截图 - 注册方法信息截图
![image-20200119101107820](/images/spring/image-20200119101107820.png) ![image-20200119101107820](../../../images/spring/image-20200119101107820.png)
@ -444,4 +444,4 @@ public class DemoInstantiationAwareBeanPostProcessor implements InstantiationAwa
这个地方已经可以看到`InstantiationAwareBeanPostProcessor`出现了,并且调用了方法`postProcessBeforeInstantiation`,此处就可以调用我们的自定义方法了 这个地方已经可以看到`InstantiationAwareBeanPostProcessor`出现了,并且调用了方法`postProcessBeforeInstantiation`,此处就可以调用我们的自定义方法了
![image-20200119101516591](/images/spring/image-20200119101516591.png) ![image-20200119101516591](../../../images/spring/image-20200119101516591.png)

@ -71,7 +71,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
## PropertyEditorRegistrar解析 ## PropertyEditorRegistrar解析
- 直接在`DatePropertyRegister`打上断点进行查看注册流程 - 直接在`DatePropertyRegister`打上断点进行查看注册流程
![image-20200117104710142](/images/spring/image-20200117104710142.png) ![image-20200117104710142](../../../images/spring/image-20200117104710142.png)
直接看调用堆栈获取调用层次 直接看调用堆栈获取调用层次
@ -111,7 +111,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
- `PropertyEditorRegistrySupport` - `PropertyEditorRegistrySupport`
![image-20200117111131406](/images/spring/image-20200117111131406.png) ![image-20200117111131406](../../../images/spring/image-20200117111131406.png)
此处对象是通过`DatePropertyRegister`传递的 此处对象是通过`DatePropertyRegister`传递的
@ -166,7 +166,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
- 在`AbstractBeanFactory`中查看变量 - 在`AbstractBeanFactory`中查看变量
![image-20200117110115741](/images/spring/image-20200117110115741.png) ![image-20200117110115741](../../../images/spring/image-20200117110115741.png)
@ -192,7 +192,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
} }
``` ```
![image-20200117110846256](/images/spring/image-20200117110846256.png) ![image-20200117110846256](../../../images/spring/image-20200117110846256.png)
@ -318,17 +318,17 @@ public class DatePropertyEditor extends PropertyEditorSupport {
![image-20200117133325461](/images/spring/image-20200117133325461.png) ![image-20200117133325461](../../../images/spring/image-20200117133325461.png)
![image-20200117141309038](/images/spring/image-20200117141309038.png) ![image-20200117141309038](../../../images/spring/image-20200117141309038.png)
![image-20200117141519123](/images/spring/image-20200117141519123.png) ![image-20200117141519123](../../../images/spring/image-20200117141519123.png)
@ -336,7 +336,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
- 属性值解析 - 属性值解析
![image-20200117142800671](/images/spring/image-20200117142800671.png) ![image-20200117142800671](../../../images/spring/image-20200117142800671.png)
```JAVA ```JAVA
@Nullable @Nullable
@ -397,7 +397,7 @@ public class DatePropertyEditor extends PropertyEditorSupport {
![image-20200117143022827](/images/spring/image-20200117143022827.png) ![image-20200117143022827](../../../images/spring/image-20200117143022827.png)
该值也是这个方法的返回`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)` 该值也是这个方法的返回`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)`

@ -185,7 +185,7 @@ public class XSDDemo {
``` ```
![image-20200109084131415](/images/spring/image-20200109084131415.png) ![image-20200109084131415](../../../images/spring/image-20200109084131415.png)
- `http://www.huifer.com/schema/user`和我们定义的xsd文件中的url相同如何找到对应的NamespaceHandler,在`META-INF/spring.handlers`中有定义, - `http://www.huifer.com/schema/user`和我们定义的xsd文件中的url相同如何找到对应的NamespaceHandler,在`META-INF/spring.handlers`中有定义,
@ -269,7 +269,7 @@ public class XSDDemo {
![image-20200109085606240](/images/spring/image-20200109085606240.png) ![image-20200109085606240](../../../images/spring/image-20200109085606240.png)
- 这里直接存在数据了,他是从什么时候加载的? - 这里直接存在数据了,他是从什么时候加载的?
@ -345,7 +345,7 @@ public class XSDDemo {
断点 断点
![image-20200109090456547](/images/spring/image-20200109090456547.png) ![image-20200109090456547](../../../images/spring/image-20200109090456547.png)
```JAVA ```JAVA
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) { public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
@ -355,13 +355,13 @@ public class XSDDemo {
`public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";` `public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";`
![image-20200109090655157](/images/spring/image-20200109090655157.png) ![image-20200109090655157](../../../images/spring/image-20200109090655157.png)
此时还是空 此时还是空
走完 走完
![image-20200109091216505](/images/spring/image-20200109091216505.png) ![image-20200109091216505](../../../images/spring/image-20200109091216505.png)
```java ```java
@Override @Override
@ -413,7 +413,7 @@ public class XSDDemo {
``` ```
![image-20200109094032421](/images/spring/image-20200109094032421.png) ![image-20200109094032421](../../../images/spring/image-20200109094032421.png)
@ -531,7 +531,7 @@ public class UserNamespaceHandler extends NamespaceHandlerSupport {
![image-20200109092801572](/images/spring/image-20200109092801572.png) ![image-20200109092801572](../../../images/spring/image-20200109092801572.png)
@ -571,7 +571,7 @@ public class UserNamespaceHandler extends NamespaceHandlerSupport {
``` ```
![image-20200109093242494](/images/spring/image-20200109093242494.png) ![image-20200109093242494](../../../images/spring/image-20200109093242494.png)
### org.springframework.beans.factory.xml.BeanDefinitionParser#parse ### org.springframework.beans.factory.xml.BeanDefinitionParser#parse
@ -629,7 +629,7 @@ public class UserNamespaceHandler extends NamespaceHandlerSupport {
} }
``` ```
![image-20200109094654409](/images/spring/image-20200109094654409.png) ![image-20200109094654409](../../../images/spring/image-20200109094654409.png)

@ -5,7 +5,7 @@
- 官方提供的测试类: `org.springframework.beans.factory.support.DefaultSingletonBeanRegistryTests` - 官方提供的测试类: `org.springframework.beans.factory.support.DefaultSingletonBeanRegistryTests`
类图 类图
![image-20200110093044672](/images/spring/image-20200110093044672.png) ![image-20200110093044672](../../../images/spring/image-20200110093044672.png)
## 注册方法解析 ## 注册方法解析
- 从名字可以看出这是一个单例对象的注册类 - 从名字可以看出这是一个单例对象的注册类
- `org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.registerSingleton` - `org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.registerSingleton`

@ -79,9 +79,9 @@
``` ```
![image-20200108081404857](/images/spring//image-20200108081404857.png) ![image-20200108081404857](../../../images/spring//image-20200108081404857.png)
![image-20200108081623427](/images/spring//image-20200108081623427.png) ![image-20200108081623427](../../../images/spring//image-20200108081623427.png)
得到本地路径,后续直接返回读取资源 得到本地路径,后续直接返回读取资源
@ -146,7 +146,7 @@
- systemId `https://www.springframework.org/dtd/spring-beans-2.0.dtd` - systemId `https://www.springframework.org/dtd/spring-beans-2.0.dtd`
![image-20200108082335031](/images/spring//image-20200108082335031.png) ![image-20200108082335031](../../../images/spring//image-20200108082335031.png)
## 总结 ## 总结

@ -49,7 +49,7 @@
读取xml配置文件 读取xml配置文件
![image-20200119141937915](/images/spring/image-20200119141937915.png) ![image-20200119141937915](../../../images/spring/image-20200119141937915.png)
@ -191,7 +191,7 @@
![image-20200119143046066](/images/spring/image-20200119143046066.png) ![image-20200119143046066](../../../images/spring/image-20200119143046066.png)
@ -199,7 +199,7 @@
获取方法`String result = getStringOrNull(bundle, code);`就是map获取 获取方法`String result = getStringOrNull(bundle, code);`就是map获取
![image-20200119144019171](/images/spring/image-20200119144019171.png) ![image-20200119144019171](../../../images/spring/image-20200119144019171.png)
@ -207,4 +207,4 @@
- 没有配置文件的情况 - 没有配置文件的情况
![image-20200119145138205](/images/spring/image-20200119145138205.png) ![image-20200119145138205](../../../images/spring/image-20200119145138205.png)

@ -60,7 +60,7 @@
``` ```
![image-20200116141838601](/images/spring/image-20200116141838601.png) ![image-20200116141838601](../../../images/spring/image-20200116141838601.png)
@ -91,7 +91,7 @@
``` ```
![image-20200116141932486](/images/spring/image-20200116141932486.png) ![image-20200116141932486](../../../images/spring/image-20200116141932486.png)

@ -46,7 +46,7 @@ public class ContextNamespaceHandler extends NamespaceHandlerSupport {
### org.springframework.context.annotation.ComponentScanBeanDefinitionParser ### org.springframework.context.annotation.ComponentScanBeanDefinitionParser
![image-20200115093602651](/images/spring/image-20200115093602651.png) ![image-20200115093602651](../../../images/spring/image-20200115093602651.png)
- 实现`BeanDefinitionParser`直接看`parse`方法 - 实现`BeanDefinitionParser`直接看`parse`方法
```java ```java
@ -299,7 +299,7 @@ public int scan(String... basePackages) {
``` ```
![image-20200115141708702](/images/spring/image-20200115141708702.png) ![image-20200115141708702](../../../images/spring/image-20200115141708702.png)
#### org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName #### org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName
@ -364,7 +364,7 @@ public class DemoService {
![image-20200115143315633](/images/spring/image-20200115143315633.png) ![image-20200115143315633](../../../images/spring/image-20200115143315633.png)
@ -398,7 +398,7 @@ public class BeanConfig {
``` ```
![image-20200115143456554](/images/spring/image-20200115143456554.png) ![image-20200115143456554](../../../images/spring/image-20200115143456554.png)

@ -48,7 +48,7 @@ public class JmsBootstrapConfiguration {
类图 类图
![image-20200304085303580](/images/springmessage/image-20200304085303580.png) ![image-20200304085303580](../../../images/springmessage/image-20200304085303580.png)
@ -329,7 +329,7 @@ public class JmsBootstrapConfiguration {
} }
``` ```
![image-20200304092154712](/images/springmessage/image-20200304092154712.png) ![image-20200304092154712](../../../images/springmessage/image-20200304092154712.png)

@ -5,7 +5,7 @@
## MessageConverter ## MessageConverter
- 消息转换接口 - 消息转换接口
- 类图如下 - 类图如下
![image-20200305085013723](/images/springmessage/image-20200305085013723.png) ![image-20200305085013723](../../../images/springmessage/image-20200305085013723.png)
- 两个方法 - 两个方法
1. fromMessage: 从消息转换到Object 1. fromMessage: 从消息转换到Object
```java ```java
@ -35,7 +35,7 @@
类图: 类图:
![image-20200305085845017](/images/springmessage/image-20200305085845017.png) ![image-20200305085845017](../../../images/springmessage/image-20200305085845017.png)
### fromMessage ### fromMessage
@ -192,6 +192,6 @@
- 两种创建方式基本相同,如果出现异常组装异常消息对象`ErrorMessage`,成功创建`GenericMessage` - 两种创建方式基本相同,如果出现异常组装异常消息对象`ErrorMessage`,成功创建`GenericMessage`
![image-20200305090846313](/images/springmessage/image-20200305090846313.png) ![image-20200305090846313](../../../images/springmessage/image-20200305090846313.png)
从类图上看`ErrorMessage`是`GenericMessage`的子类 从类图上看`ErrorMessage`是`GenericMessage`的子类

@ -137,11 +137,11 @@ public class Application {
- `SpringFactoriesLoader.loadFactoryNames(type, classLoader)` 是spring提供的方法主要目的是读取`spring.factories`文件 - `SpringFactoriesLoader.loadFactoryNames(type, classLoader)` 是spring提供的方法主要目的是读取`spring.factories`文件
- 读取需要创建的内容 - 读取需要创建的内容
![image-20200318080601725](/images/SpringBoot/image-20200318080601725.png) ![image-20200318080601725](../../../images/SpringBoot/image-20200318080601725.png)
- 创建完成 - 创建完成
![image-20200318080901881](/images/SpringBoot/image-20200318080901881.png) ![image-20200318080901881](../../../images/SpringBoot/image-20200318080901881.png)
@ -151,21 +151,21 @@ public class Application {
`SharedMetadataReaderFactoryContextInitializer` `SharedMetadataReaderFactoryContextInitializer`
![image-20200318081112670](/images/SpringBoot/image-20200318081112670.png) ![image-20200318081112670](../../../images/SpringBoot/image-20200318081112670.png)
- 同样的再找一个`DelegatingApplicationContextInitializer` - 同样的再找一个`DelegatingApplicationContextInitializer`
![image-20200318081322781](/images/SpringBoot/image-20200318081322781.png) ![image-20200318081322781](../../../images/SpringBoot/image-20200318081322781.png)
- 下图中的所有类都有Order数值返回 - 下图中的所有类都有Order数值返回
排序前: 排序前:
![image-20200318081352639](/images/SpringBoot/image-20200318081352639.png) ![image-20200318081352639](../../../images/SpringBoot/image-20200318081352639.png)
排序后: 排序后:
![image-20200318081458019](/images/SpringBoot/image-20200318081458019.png) ![image-20200318081458019](../../../images/SpringBoot/image-20200318081458019.png)
@ -378,7 +378,7 @@ public class Application {
### exceptionReporters ### exceptionReporters
![image-20200318085243888](/images/SpringBoot/image-20200318085243888.png) ![image-20200318085243888](../../../images/SpringBoot/image-20200318085243888.png)
@ -465,9 +465,9 @@ public class Application {
![image-20200318090128983](/images/SpringBoot/image-20200318090128983.png) ![image-20200318090128983](../../../images/SpringBoot/image-20200318090128983.png)
![image-20200318090312626](/images/SpringBoot/image-20200318090312626.png) ![image-20200318090312626](../../../images/SpringBoot/image-20200318090312626.png)
@ -504,7 +504,7 @@ public class Application {
- 数据结果 - 数据结果
![image-20200318090935285](/images/SpringBoot/image-20200318090935285.png) ![image-20200318090935285](../../../images/SpringBoot/image-20200318090935285.png)
- 子类的具体实现不展开了 - 子类的具体实现不展开了
@ -534,7 +534,7 @@ public class Application {
- `primarySources` 就是我们的项目启动类,在`SpringApplication`的构造器中有`this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))` - `primarySources` 就是我们的项目启动类,在`SpringApplication`的构造器中有`this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))`
![image-20200318091558233](/images/SpringBoot/image-20200318091558233.png) ![image-20200318091558233](../../../images/SpringBoot/image-20200318091558233.png)
@ -608,7 +608,7 @@ private int load(Object source) {
- 通过前文我们已经知道 `source`就是一个class - 通过前文我们已经知道 `source`就是一个class
![image-20200318092027020](/images/SpringBoot/image-20200318092027020.png) ![image-20200318092027020](../../../images/SpringBoot/image-20200318092027020.png)

@ -36,7 +36,7 @@ public @interface ConfigurationPropertiesScan {}
## ConfigurationPropertiesScanRegistrar ## ConfigurationPropertiesScanRegistrar
![image-20200323094446756](/images/SpringBoot/image-20200323094446756.png) ![image-20200323094446756](../../../images/SpringBoot/image-20200323094446756.png)
- debug没有抓到后续补充 - debug没有抓到后续补充
@ -156,13 +156,13 @@ public @interface EnableConfigurationProperties {
- 先看输入参数 **metadata** - 先看输入参数 **metadata**
![image-20200323134135926](/images/SpringBoot/image-20200323134135926.png) ![image-20200323134135926](../../../images/SpringBoot/image-20200323134135926.png)
- getTypes结果 - getTypes结果
![image-20200323134325955](/images/SpringBoot/image-20200323134325955.png) ![image-20200323134325955](../../../images/SpringBoot/image-20200323134325955.png)
@ -231,7 +231,7 @@ public @interface EnableConfigurationProperties {
## ConfigurationPropertiesBindingPostProcessor ## ConfigurationPropertiesBindingPostProcessor
![image-20200323095626953](/images/SpringBoot/image-20200323095626953.png) ![image-20200323095626953](../../../images/SpringBoot/image-20200323095626953.png)
@ -356,15 +356,15 @@ public @interface EnableConfigurationProperties {
- `annotation` - `annotation`
![image-20200323104711545](/images/SpringBoot/image-20200323104711545.png) ![image-20200323104711545](../../../images/SpringBoot/image-20200323104711545.png)
- `bindType` - `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)
- 此时数据还没有进去 - 此时数据还没有进去
@ -378,7 +378,7 @@ public @interface EnableConfigurationProperties {
直接看结果 直接看结果
![image-20200323105155998](/images/SpringBoot/image-20200323105155998.png) ![image-20200323105155998](../../../images/SpringBoot/image-20200323105155998.png)
- 上述配置和我在配置文件中写的配置一致 - 上述配置和我在配置文件中写的配置一致
@ -425,7 +425,7 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
} }
``` ```
![image-20200323105830138](/images/SpringBoot/image-20200323105830138.png) ![image-20200323105830138](../../../images/SpringBoot/image-20200323105830138.png)
@ -501,15 +501,15 @@ 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)
@ -546,7 +546,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
- 最终获取得到的处理器 - 最终获取得到的处理器
![image-20200323110603959](/images/SpringBoot/image-20200323110603959.png) ![image-20200323110603959](../../../images/SpringBoot/image-20200323110603959.png)
@ -586,7 +586,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
![image-20200323112945449](/images/SpringBoot/image-20200323112945449.png) ![image-20200323112945449](../../../images/SpringBoot/image-20200323112945449.png)

@ -20,7 +20,7 @@ public enum LogLevel {
- `org.springframework.boot.logging.java.JavaLoggingSystem` - `org.springframework.boot.logging.java.JavaLoggingSystem`
![image-20200323144523848](/images/SpringBoot/image-20200323144523848.png) ![image-20200323144523848](../../../images/SpringBoot/image-20200323144523848.png)
```JAVA ```JAVA
static { static {
@ -141,7 +141,7 @@ public static LoggingSystem get(ClassLoader classLoader) {
![image-20200323151409473](/images/SpringBoot/image-20200323151409473.png) ![image-20200323151409473](../../../images/SpringBoot/image-20200323151409473.png)
@ -151,7 +151,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` 1. `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent`
@ -395,13 +395,13 @@ 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)
- 此时配置文件地址出现了 - 此时配置文件地址出现了

@ -12,11 +12,11 @@
2. 全局搜索yml 2. 全局搜索yml
![image-20200319083048849](/images/SpringBoot/image-20200319083048849.png) ![image-20200319083048849](../../../images/SpringBoot/image-20200319083048849.png)
3. 换成`properties`搜索 3. 换成`properties`搜索
![image-20200319083140225](/images/SpringBoot/image-20200319083140225.png) ![image-20200319083140225](../../../images/SpringBoot/image-20200319083140225.png)
4. 我们以`yml`为例打上断点开始源码追踪 4. 我们以`yml`为例打上断点开始源码追踪
@ -26,7 +26,7 @@
看到调用堆栈 看到调用堆栈
![image-20200319083345067](/images/SpringBoot/image-20200319083345067.png) ![image-20200319083345067](../../../images/SpringBoot/image-20200319083345067.png)
@ -40,9 +40,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)
@ -91,15 +91,15 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader` - 搜索目标: `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字符串的时候找到的类是一样的。说明搜索方式没有什么问题。 观察发现里面有一个`YamlPropertySourceLoader`和我们之前找yml字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
![image-20200319084357652](/images/SpringBoot/image-20200319084357652.png) ![image-20200319084357652](../../../images/SpringBoot/image-20200319084357652.png)
初始化完成,后续进行解析了 初始化完成,后续进行解析了
@ -139,7 +139,7 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
### initializeProfiles ### initializeProfiles
- 初始化`private Deque<Profile> profiles;` 属性 - 初始化`private Deque<Profile> profiles;` 属性
- ![image-20200319084902957](/images/SpringBoot/image-20200319084902957.png) - ![image-20200319084902957](../../../images/SpringBoot/image-20200319084902957.png)
@ -170,7 +170,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
- 资源路径可能性 - 资源路径可能性
![image-20200319085446640](/images/SpringBoot/image-20200319085446640.png) ![image-20200319085446640](../../../images/SpringBoot/image-20200319085446640.png)
@ -237,7 +237,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
![image-20200319090446231](/images/SpringBoot/image-20200319090446231.png) ![image-20200319090446231](../../../images/SpringBoot/image-20200319090446231.png)

@ -52,7 +52,7 @@ public @interface EnableAutoConfiguration {
- 类图 - 类图
![image-20200320150642022](/images/SpringBoot/image-20200320150642022.png) ![image-20200320150642022](../../../images/SpringBoot/image-20200320150642022.png)
## getAutoConfigurationMetadata() ## getAutoConfigurationMetadata()
@ -106,7 +106,7 @@ public @interface EnableAutoConfiguration {
``` ```
![image-20200320160423991](/images/SpringBoot/image-20200320160423991.png) ![image-20200320160423991](../../../images/SpringBoot/image-20200320160423991.png)
@ -138,11 +138,11 @@ public @interface EnableAutoConfiguration {
![image-20200320162835665](/images/SpringBoot/image-20200320162835665.png) ![image-20200320162835665](../../../images/SpringBoot/image-20200320162835665.png)
同样找一下redis 同样找一下redis
![image-20200320163001728](/images/SpringBoot/image-20200320163001728.png) ![image-20200320163001728](../../../images/SpringBoot/image-20200320163001728.png)
@ -236,13 +236,13 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process` - `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200320163806852](/images/SpringBoot/image-20200320163806852.png) ![image-20200320163806852](../../../images/SpringBoot/image-20200320163806852.png)
再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作 再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作
关注 `AnnotationMetadata annotationMetadata` 存储了一些什么 关注 `AnnotationMetadata annotationMetadata` 存储了一些什么
![image-20200320164145286](/images/SpringBoot/image-20200320164145286.png) ![image-20200320164145286](../../../images/SpringBoot/image-20200320164145286.png)
这里简单理解 这里简单理解
@ -304,7 +304,7 @@ public class RedisProperties {
![image-20200320171138431](/images/SpringBoot/image-20200320171138431.png) ![image-20200320171138431](../../../images/SpringBoot/image-20200320171138431.png)
@ -324,7 +324,7 @@ public class RedisProperties {
``` ```
![image-20200320171734270](/images/SpringBoot/image-20200320171734270.png) ![image-20200320171734270](../../../images/SpringBoot/image-20200320171734270.png)
- 第一个是我自己写的一个测试用 - 第一个是我自己写的一个测试用
@ -390,7 +390,7 @@ public class RedisProperties {
![image-20200323080611527](/images/SpringBoot/image-20200323080611527.png) ![image-20200323080611527](../../../images/SpringBoot/image-20200323080611527.png)
@ -401,7 +401,7 @@ public class RedisProperties {
``` ```
![image-20200323081009823](/images/SpringBoot/image-20200323081009823.png) ![image-20200323081009823](../../../images/SpringBoot/image-20200323081009823.png)
### checkExcludedClasses ### checkExcludedClasses
@ -477,7 +477,7 @@ public class RedisProperties {
![image-20200323081903145](/images/SpringBoot/image-20200323081903145.png) ![image-20200323081903145](../../../images/SpringBoot/image-20200323081903145.png)
@ -493,7 +493,7 @@ public class RedisProperties {
![image-20200323082553595](/images/SpringBoot/image-20200323082553595.png) ![image-20200323082553595](../../../images/SpringBoot/image-20200323082553595.png)
@ -521,13 +521,13 @@ public class RedisProperties {
``` ```
![image-20200323083149737](/images/SpringBoot/image-20200323083149737.png) ![image-20200323083149737](../../../images/SpringBoot/image-20200323083149737.png)
- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);` - `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`** - `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`**
@ -547,7 +547,7 @@ public class RedisProperties {
![image-20200323083656670](/images/SpringBoot/image-20200323083656670.png) ![image-20200323083656670](../../../images/SpringBoot/image-20200323083656670.png)
@ -565,7 +565,7 @@ public class RedisProperties {
![image-20200323084922159](/images/SpringBoot/image-20200323084922159.png) ![image-20200323084922159](../../../images/SpringBoot/image-20200323084922159.png)
- 后续的一些行为相对简单,直接放个源码了. - 后续的一些行为相对简单,直接放个源码了.

Loading…
Cancel
Save