# Dubbo2.7.7源码 **Repository Path**: hu_tutu2/dubbo-2.7.7-source-code ## Basic Information - **Project Name**: Dubbo2.7.7源码 - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-12-09 - **Last Updated**: 2024-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Dubbo源码解析 * SPI机制 SPI全称为 Service Provider Interface,一种解耦接口和实现的手段,其实现原理是将接口的实现类全名称配置在配置文件中,程序运行阶段去读取配置文件加载实现类 **jdk SPI 和 Dubbo SPi** * `Dubbo`为什么使用SPI `dubbo`作为一个rpc框架,在它发送RPC请求时,整个过程会经历很多个关键事件节点,比如集群容错,负载均衡,数据序列化,信协议编码,网络传输等,每个关键节点都有抽象出对应的接口,**而且有多种不同的实现,且用户可自行扩展,那实际运行阶段dubbo如何根据用户的配置参数来选择具体的实现呢,这就促使dubbo需要一种可插拔的接口实现发现机制** **`dubbo`采用微内核架构,将每一个功能接口当作一个可插拔的扩展点接口,内核层面只负责按流程组装并引导执行每个扩展点接口具体的功能和逻辑由具体的扩展点实现来完成,提高了系统的扩展性和灵活性,而spi就是实现微内核的手段** * `Dubbo`为什么不沿用`jdk-spi` 1.jdk-spi虽然实现了解耦,能够做到接口与实现分离但在使用上只能迭代获取所有实现,无法按需获取 2.jdk-spi在迭代加载的过程中就创建所有实现对象,也不管当前是否使用 3.jdk-spi在并发安全处理上并未做任何设计 4.除了想要使用方便,按需获取等特性之外,`dubbo`还想根据自己的场景添加更多功能特性在里面 * `Dubbo`是如何解决的 1.配置文件改成key = value的形式 再程序的中通过key获取对应的实现 ![`](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104163133544.png) * `Dubbo SPI` 如何的使用 1.在接口上添加 @SPI注解,告诉`dubbo`该接口可用其 SPI机制来加载 2.在`META-INF/dubbo,META-INF/dubbo/internal,META-INF/services `中任何一个目录下创建一个以接口全限定名称为文件名的文件,在文件中按K=V的形式配置每个扩展点实现的全限定名称,其中K是自定义的 2.使用 `ExtensionLoader`的api是获取 **例子** 在接口添加`@SPI`的注解 ```java @SPI("bumblebee") //bumblebee表示默认加载该对象实例 public interface Robot { void sayHello(); } ``` 实现类 ```java public class Bumblebee implements Robot { @Override public void sayHello() { System.out.println("Hello, I am Bumblebee."); } } public class OptimusPrime implements Robot { private Protocol protocol; public void setProtocol(Protocol protocol) { this.protocol = protocol; } @Override public void sayHello() { System.out.println(protocol); System.out.println("Hello, I am Optimus Prime."); } } public class RobotWrapper implements Robot { private Robot robot; public RobotWrapper(Robot robot) { this.robot = robot; } @Override public void sayHello() { System.out.println("----提前准备----"); robot.sayHello(); System.out.println("----收尾工作----"); } } ``` 配置文件 ![image-20241104163736489](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104163736489.png) 测试 ```java //测试dubbo spi机制 @Test public void sayHello() throws Exception { //1、获得接口的ExtentionLoader ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); //2、根据指定的名字获(key)取对应的实例 Robot robot = extensionLoader.getExtension("optimusPrime"); robot.sayHello(); /*Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello();*/ Robot robot2 = extensionLoader.getDefaultExtension(); robot2.sayHello(); } ``` * **`Dubbo SPI `高级特性自动包装`Wrapper`** Dubbo SPl提供了类似Spring AOP的功能,可以对扩展点进行增强,称之为Wrapper (就是AOP的功能 对实现类进行前置增强等) 提供`wrapper`类,`wrapper`类与扩展点实现同一接口,并提供扩展点接口参数类型的构造函数【装饰者模式】 例如 编写`Wrapper` ![image-20241104164531858](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104164531858.png) 配置文件添加`wrapper` ![image-20241104164707602](./assets/image-20241104164707602.png) **如果有多个wrapper的话 都会被每个wrapper包**装 我们可以通过添加`@Activate`注解的order字段来指定我们增强的顺序 还可以通过`@Wrapper`注解的match来指定增强谁和不增强谁 * **`Dubbo SPI`自动装配** Dubbo SPl提供了类似Spring ioc的功能,可以对扩展点进行依赖注入 * 装配的实现是自适应扩展点 后面又说 * 在一个扩展点中通过set方法去注入另外 一个扩展点 * 在wrapper中也同样支持通过set进行依赖注入 * **`Dubbo SPI`自适应** 我们在测试中 在这里通过key的值获取对应的实例 ```java //1、获得接口的ExtentionLoader ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); //2、根据指定的名字获(key)取对应的实例 Robot robot = extensionLoader.getExtension("optimusPrime"); ``` 但是在框架的底层 这里不会是固定字符串的 而是通过用户配置 然后框架读取配置文件 根据用户的定义 选择对应的实现 `Dubbo`如何的实现的 使用自适应 自适应类实现业务的接口 在实现的方法中读取配置 然后对应的实现 然后在代码中获取自适应对应 调用自适应的实现的方法 ![image-20241104173554263](./assets/image-20241104173554263.png) ``` dubbo的内置接口已经有对应的自适应 会自己的生成 外部的接口 可以让dubbo生成 或者 自己编写 ``` **自适应实例的编写** 1.核心要素就是在自适应实现类上标注 @Adaptive 注解 2.配置文件中同样需要把自适应类配置进去 3.每个接口的自适应类只能有一个 **框架生成** 1.在扩展点接口方法上标注 @Adaptive 注解,哪个方法需要在运行时自适应执行则添加 2.方法必须要有URL类型的参数,或者参数有getUrl()方法返回一个URL对象,URL是dubbo存储配置的容器 **`Url`是dubbo的配置信息** ![image-20241104174735042](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104174735042.png) 3.`dubbo`就会从上面的例子的获取`stove`为key的值 然后获取对应的实现 我们在测试 ![image-20241104175543185](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104175543185.png) * `Dubbo`自动激活 **在前面讨论的所有特性中大都是集中在一个场景:需要从众多扩展点实现中根据name挑出一个,但是有一些场景并不是万里挑一的,而是可以多个扩展点实现共存并且项目初始化后就可以直接使用的,比如:Filter,Listener等** **所谓自动激活其实就是让某些扩展点默认就可以获取并使用,并不需要根据它们的key来逐一获取,这样可以拿到已激活的扩展点集合** 1.**想要默认激活某扩展点**,需要给它添加`@Activate` 注解 2,由于`dubbo`的主要场景是`RPC`,分为消费端和提供方,所以可以通过`Activate`的`group`属性指定该扩展点在哪一端激活 3,通过`Activate`的`value`属性指定更多激活的判断条件 4.通过`getActivateExtension()`可以获取扩展点的集合 ## 领域模型 * `Invocation`是会话域,它持有调用过程中的变量,比如方法名,参数等 ![image-20241104223609710](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104223609710.png) ![image-20241104223624370](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104223624370.png) * `Invoker` **Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现**。 会包装服务提供的`Impl`或者消费者的接口 成为一个`invoker` **本地 Invoker**:在本地 JVM 中调用的 `Invoker`,通常是服务提供者的本地实现。 **远程 Invoker**:用于通过网络调用远程服务的 `Invoker`,包括支持各种协议的实现(例如 Dubbo、HTTP、RESTful 等)。 `invoker`的工作流程 **服务消费者发起请求**:服务消费者通过 Dubbo 提供的代理类调用远程服务,实际上是调用了一个 `Invoker` 对象的 `invoke()` 方法。 **Invoker 执行调用**:`Invoker.invoke()` 会触发对实际服务的调用,调用过程中可能涉及负载均衡、路由选择、协议的适配、序列化、网络传输等多个步骤。 **调用结果返回**:最终,`Invoker.invoke()` 返回调用结果,结果会通过 `Result` 对象封装,包含返回值或者异常信息。 * `Protocol` 是服务域,它是` Invoker `暴露和引用的主功能入口,它负责` Invoker `的生命周期管理 ![image-20241104224845148](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104224845148.png) * 整体设计 ![img](https://cn.dubbo.apache.org/imgs/dev/dubbo-framework.jpg) - 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。 - 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。 - 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。 - 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。 **各层的说明** - **config 配置层**:对外配置接口,以 `ServiceConfig`, `ReferenceConfig` 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 - **proxy 服务代理层**:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 `ServiceProxy` 为中心,扩展接口为 `ProxyFactory` - **registry 注册中心层**:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 `RegistryFactory`, `Registry`, `RegistryService` - **cluster 路由层**:封装多个提供者的路由及负载均衡,并桥接注册中心,以 `Invoker` 为中心,扩展接口为 `Cluster`, `Directory`, `Router`, `LoadBalance` - **monitor 监控层**:RPC 调用次数和调用时间监控,以 `Statistics` 为中心,扩展接口为 `MonitorFactory`, `Monitor`, `MonitorService` - **protocol 远程调用层**:封装 RPC 调用,以 `Invocation`, `Result` 为中心,扩展接口为 `Protocol`, `Invoker`, `Exporter` - **exchange 信息交换层**:封装请求响应模式,同步转异步,以 `Request`, `Response` 为中心,扩展接口为 `Exchanger`, `ExchangeChannel`, `ExchangeClient`, `ExchangeServer` - **transport 网络传输层**:抽象 mina 和 netty 为统一接口,以 `Message` 为中心,扩展接口为 `Channel`, `Transporter`, `Client`, `Server`, `Codec` - **serialize 数据序列化层**:可复用的一些工具,扩展接口为 `Serialization`, `ObjectInput`, `ObjectOutput`, `ThreadPool` `EnableDubbo`注解 ![image-20241104230639800](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104230639800.png) 从这个注解开始 ![image-20241104230737034](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104230737034.png) ![image-20241104231005316](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241104231005316.png) **回到`EnaleDubbo`的注解** * DubboComponentScan ![image-20241105100704654](./assets/image-20241105100704654.png) ![image-20241105101016485](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105101016485.png) `ServiceAnnotationBeanPostProcessor` 继承于 ![image-20241105101521999](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105101521999.png) ![image-20241105101647879](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105101647879.png) ![image-20241105101840089](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105101840089.png) 最后将扫描到的注解的类添加到容器的里面 `scanner.scan`就会扫描到然后注册到容器中 现在看我们的容器的实例就有我们加`@DubboService`注解的实现类 ![image-20241105104613444](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105104613444.png) 然后会调用该方法 `registerServiceBean` ![image-20241105103732527](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105103732527.png) 在scan方法中已经把加上注解的实现类放入容器中 然后如果我们的请求过来如何 知道调用 哪一个的`Impl`呢 ?或者说如何? 这就需要一个包装类 他也会向容器的里面的注册 `ServiceBean` ![image-20241105104329381](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105104329381.png) 在容器的`key = ServiceBean:接口名称 value = 实现类` 在`registerServiceBean`方法就是将一个一个的`serviceBean`注册到容器的里面 ![image-20241105104954699](./assets/image-20241105104954699.png) ![image-20241105105224350](./assets/image-20241105105224350.png) `DubboComponentScan`注解导入了 `DubboComponentScanRegistrar`类 该类实现了spring的 `ImportBeanDefinitionRegistrar` 该回调方法用于向容器的里面添加自定义的bean 回调方法`registerBeanDefinitions`将 `ServiceAnnotationBeanPostProcessor`这个类放入容器的里面 该类继承于`ServiceClassPostProcessor` 并且实现了 spring`BeanDefinitionRegistryPostProcessor` 相当于BeanDefition的后置处理器 在回调方法中`postProcessBeanDefinitionRegistry`中 注册了Dubbo的启动器 并且调用了 `registerServiceBeans`的方法 该方法扫描了配置的包路径 然后添加注解扫描器 将`@DubboService @Service`等添加到容器的里面 然后调用`registerServiceBean`方法将 `ServiceBean`注入到容器的里面 * 服务端服务端的注册和netty的启动 在`ServiceClassPostProcessor` 的 方法`postProcessBeanDefinitionRegistry`中把`DubboBootstrapApplicationListener`的启动器注入到了容器中 ![image-20241105160811103](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105160811103.png) 在`dubbo`启动类中有回调方法`onApplicationContextEvent`当spring的容器注入完以后就会执行这个方法 在回调方法中调用该方法 ![image-20241105161003271](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105161003271.png) ​ 然后调用`export`方法 在`exportService`中遍历每一个`ServiceBean` ![image-20241105161235562](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105161235562.png) ![image-20241105161851208](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105161851208.png) 在`export`方法中调用`doExport`方法 在`doExport`中调用`doExport`方法 ![image-20241105161406408](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105161406408.png) 在`doExportUrls`中做了这几件事情 1.加载注册中心的URL对象 可以看见协议是`register` ![image-20241105162055512](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105162055512.png) 2.在注册中心注册 `protocols`是协议的集合 ![image-20241105162230198](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105162230198.png) `doExportUrlsFor1Protocol`方法 ![image-20241105162617092](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105162617092.png) 导出服务提供者的主机和通讯端口封装成`url`对象 ![image-20241105162930633](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105162930633.png) 然后就到了代码的核心 ![image-20241105163047586](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105163047586.png) 首先是下面这段代码 在这段代码使用`Dubbo SPI`和自适应 用户不配置默认的调用的是 `JavassistProxyFactory`的`invoker`方法的实现 这个方法的作用 使用 **ProxyFactory** 来创建一个 `Invoker` 对象,实际实现的是根据服务引用(`ref`)、服务接口(`interfaceClass`)以及一些额外的 URL 参数来创建一个服务的代理对象,进而实现服务的调用 暂且可以认为这是服务的实现 ```java Invoker invoker = PROXY_FACTORY .getInvoker(ref, (Class) interfaceClass , registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); ``` ![image-20241105163605092](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105163605092.png) 然后再次进行对这个`invoker`进行包装 ```java DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); ``` 然后是下面这段代码,使用了自适应的机制通过参数`wrapperInvoker`的url参数来选择对应的实现 ![image-20241105164039168](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105164039168.png) ```java Exporter exporter = PROTOCOL.export(wrapperInvoker); exporters.add(exporter); ``` ![image-20241105165410012](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105165410012.png) `PROTOCOL.export()`中首先获取`registryUrl 和 providerUrl`的地址 ![image-20241105170204273](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105170204273.png) 然后调用下面的方法 ![image-20241105171830303](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105171830303.png) **暴露服务到本地**的含义 ```java 在 Dubbo 中,当服务提供者希望将一个服务暴露出去时,通常是通过网络协议(例如 `dubbo` 协议)暴露,供消费者通过远程调用访问。但有时我们希望在同一个 JVM 中让服务提供者和消费者能够直接进行方法调用,而不通过网络传输,这就需要使用 **本地协议**(例如 `injvm` 协议)。doLocalExport 正是用来实现将服务暴露为 **本地调用** 的。 例如 假设你有一个 UserService 服务,服务提供者和服务消费者都在同一个 JVM 中运行。为了避免网络通信开销,服务提供者希望将服务暴露为本地服务,这样服务消费者可以直接通过方法调用来访问该服务,而不经过网络层。 public class UserServiceImpl implements UserService { @Override public String getUserInfo(String userId) { return "User info for " + userId; } } public class LocalExportExample { public void exportService() { // 创建一个原始的 Invoker 对象 Invoker originInvoker = new ReflectedInvoker<>(new UserServiceImpl(), UserService.class); // providerUrl 是暴露服务时需要的 URL URL providerUrl = new URL("injvm", "localhost", 0, "com.example.UserService"); // 调用 doLocalExport,将服务暴露为本地服务 doLocalExport(originInvoker, providerUrl); } public void doLocalExport(Invoker originInvoker, URL providerUrl) { // 将服务通过 injvm 协议暴露为本地服务 Exporter exporter = PROTOCOL.export(originInvoker); // 本地暴露成功 } } 然后在调用本地服务 public class UserServiceConsumer { public void callService() { // 获取 UserService 的本地代理 UserService userService = (UserService) proxyFactory.getProxy(invoker); // 直接调用本地方法 String result = userService.getUserInfo("12345"); System.out.println(result); // 输出: User info for 12345 } } ``` * `doLocalExport`两个作用将服务暴露在本地并且开始我们的netty服务 进入`doLocalExport`中调用这个 ![image-20241105185555552](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105185555552.png) 我们debug到`DubboProtocol`的export的方法中 ![image-20241105185752693](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105185752693.png) 然后进入`craeteServer`中 在方法中 实现构建url对象 ![image-20241105190120805](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190120805.png) 然后调用`ExchangeServer 的 bind方法绑定端口` ![image-20241105190234651](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190234651.png) 然后调用这个方法![image-20241105190501976](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190501976.png) 在这个方法中调用 ![image-20241105190526949](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190526949.png) ![image-20241105190626221](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190626221.png) 在这个方法中就创建了一个`NettyServer`对象 ![image-20241105190820893](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105190820893.png) `netty`启动完成以后 回到`RegistryProtocol`的export方法中 接下来将提供者注册到服务中心去 ![image-20241105191510942](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105191510942.png) 然后debug进去这个方法中 最后调用`doRegister`方法 调用`zk`的java的客户端注册 ![image-20241105191622235](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105191622235.png) 服务端netty的启动和服务的注册的流程已经结束了 **DubboBootstrapApplicationListener 类是dubbo的启动类 内部当spring ioc注入完成以后就会触发回调 最后调用了DubboBootstrap的start的方法 该方法有调用该类的exportServices的方法 在这个方法遍历容器的ServiceBean对象 执行每个对象的export方法 然后export对象 调用doExport 然后调用doUrls 在这个方法中根据获取的协议和注册中心的url 将前两个作为参数调用 doExportUrlsFor1Protocol 方法 该方法首先 获取 服务的ip和port 然后将服务的impl导出成invoker 然后invoker作为参数 调用Protocol的export方法 根据自适应获取RegisterProtocol的export方法 在这方法首先调用该类的doLocalExport方法 该方法的作用是服务暴露在本地 然后开启服务 然后调用该类的register方法 将服务注册到注册中心 在doLocalExport方法中首先调用Dubboprotocol的export方法 然后调用该类的openServer 然后调用createServer最后在createServer调用exchange的bind方法 然后调用transport的bind 然后基于nettyTransport创建了NettyServer 并且开启了netty** ## 消费者 服务的消费者 还是从`@EnableDubbo`注解开始 `@EnableDubboConfig`已经说过了 将配置加载到配置类中 并且放入IOC的容器的里面 `@DubboComponentScan`开始 导入`DubboComponentScanRegistrar`类 该类的回调方法 ![image-20241105225553115](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105225553115.png) ![image-20241105225713998](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105225713998.png) `ReferenceAnnotationBeanPostProcessor`中 继承了`AbstractAnnotationBeanPostProcessor` 并且在`ReferenceAnnotationBeanPostProcessor`的无参构造中 调用了父类的构造方法 ```java public ReferenceAnnotationBeanPostProcessor() { super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class); } ``` ![image-20241105231357356](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105231357356.png) 在父类的`annotationTypes`就会被赋值 ![image-20241105231449335](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105231449335.png) `AbstractAnnotationBeanPostProcessor`分别继承于`InstantiationAwareBeanPostProcessorAdapter`和实现了`MergedBeanDefinitionPostProcessor` 对应的回调分别是`postProcessPropertyValues`和`postProcessMergedBeanDefinition` 后者的回调先于前者执行 ![image-20241105230439779](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105230439779.png) `buildAnnotatedMetadata()`方法 ![image-20241105230759858](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105230759858.png) `findFieldAnnotationMetadata()`中调用`ReflectionUtils.doWithFields`方法 传入目标Bean的class对象和lamdba表达式 在`doWith()方法中` 对传入的字段对象检查是否有`annotationTypes`中的注解 如果存在放入element集合上 最后在调用该方法 时候对把返回值封装成`Metadata` ![image-20241105231804323](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105231804323.png) ![image-20241105231900884](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105231900884.png) 这样就将字段的标有对应注解的字段扫描到 然后封装成metadata对象 放入缓存 然后我们来到第二个回调方法上 `postProcessPropertyValues` **因为我们第一步已经使用过findInjectionMetadata 然后构建了该bean的metadata并且存入缓存** ![image-20241105232309344](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105232309344.png) 看下`InjectMetadata`的结构 ![image-20241105234057062](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105234057062.png) 然后调用**metadata**的`inject`方法 ![image-20241105234218664](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105234218664.png) 这个`inject`方法就清晰了 ![image-20241105234428546](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105234428546.png) 现在关注就是`getInjectedObject`该方法是如何生成代理的 `getInjectedObject`先从缓存中查找是否有代理的缓存 如果存在直接返回 不存在调用`doGetInjectedBean` ![image-20241105234954372](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241105234954372.png) ![image-20241106152313141](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106152313141.png) ![image-20241106152425461](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106152425461.png) ![image-20241106152512366](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106152512366.png) 调用`init()`一些代码 直接看为ref属性赋值的方法 在init()方法中调用` createProxy`方法为ref属性赋值 ![image-20241106152635232](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106152635232.png) 然后进入该方法 ![image-20241106152953157](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106152953157.png) ![image-20241106153331600](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106153331600.png) 然后就会调用`RegisterProtocol的refer()方法` 然后会调用`doRefer`方法 ![image-20241106153511870](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106153511870.png) 在`dorefer`的方法中首先创建了一个 `RegistryDirectory`的对象 该对象回调`notify`用于监听服务的变更 当该对象订阅了服务以后就会触发`notify` ![image-20241106153539901](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106153539901.png) ![image-20241106153829768](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106153829768.png) 当我们执行完`register.register()`方法 查看我们的注册中心 我们注册中心就会有consumer的地址了 ![image-20241106154056665](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154056665.png) 我们在执行`directory.subscribe`方法之前先给回调方法`notify`打上断点 因为是第一次订阅 所有会立刻触发`notify`的回调 ![image-20241106154342212](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154342212.png) 然后就会调用`refreshOverrideAndInvoker` ![image-20241106154411120](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154411120.png) 在该方法中最后有调用`refreshInvoker` 然后最后调用`toInvokers`方法![image-20241106154604813](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154604813.png) 在该方法中就会调用`protocol`层 ![image-20241106154727618](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154727618.png) 最后调用 ![image-20241106154844015](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106154844015.png) 然后调用这个方法 ![image-20241106155705944](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106155705944.png) ![image-20241106155842482](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106155842482.png) 然后调用Exchanger层 ![image-20241106155913094](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106155913094.png) 最后就会调用`Transports`层 然后创建一个`Netty`客户端 ![image-20241106155933367](https://hututu345.oss-cn-beijing.aliyuncs.com/typora/image-20241106155933367.png)