(三)DUBBO源码解析----@Service provider发布服务过程
先祭概述
本文将从 spring如何加载实例入手:
- 先分析如何加载spring普通实例
- 再分析如何加载 dubbo 服务
- 然后分析加载 dubbo 服务后,如何发布服务
本文分析的dubbo 服务注册中心是 zookeeper,其它注册中心(如redis)应该差别不大。 本文面向的读者,首先应该会用 springboot,并且会用 dubbo,但是对他们的源代码或者原理不是很清楚。
再祭版本
springboot 版本 2.x
spring 版本5.x
dubbo 版本 2.6.0
OK , let’s begin!!
HouseDao 是一个普通的 spring 服务, 其@service 注解来自 spring
import org.springframework.stereotype.Service; @Service public class HouseDao { public void build(){ System.out.println("house build"); } }
HelloServiceImpl 是一个 dubbo provider, 其@Service 注解来自 dubbo
import com.alibaba.dubbo.config.annotation.Service; @Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String word) { return "hello " + word; } }
part 1: 我们首先来看普通服务HouseDao(spring的 @Service)是如何加载的
在构造方法中打一个断点:
找到初始化堆栈:
我们依次点进去,发现初始化入口在DefaultListableBeanFactory的 preInstantiateSingletons()方法中:
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } //所有需要实例化的服务放在列表中 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { //省略大量代码..... } else { //本行代码实例化bean getBean(beanName); } } } // ... }
所以我们需要搞清楚this.beanDefinitionNames 是何时被赋值的,找到这个beanDefinitionNames的声明处,然后找到他所有的引用:
如上图,发现这个beanDefinitionNames变量引用虽然很多,但是写入的引用只有两处,其中一出还是删除, 那很显然, 复制就是在另外一处啦,也就是registerBeanDefinition()方法。
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //省略代码。。。 BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { //省略大量代码。。。。 } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { //省略。。。 // beanName 是方法参数 updatedDefinitions.add(beanName); //beanDefinitionNames 是在此处赋值 this.beanDefinitionNames = updatedDefinitions; //省略。。。 } } else { //省略代码。。。 } this.frozenBeanDefinitionNames = null; } //省略代码。。。。 }
可以看到关键参数 beanName 是有上游方法传过来的。OK ,那么在 这一行加一个有条件的断点,当 beanName 为 houseDao的时候断住,看看这时候上游代码发生了什么。
执行到断点位置时,我们打开调用栈,依次观察下去,会发现两个重要的位置,就是下面圈红的位置:
第二个红框代码是:
// Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
第一个红框代码是:
processConfigBeanDefinitions(registry);
总体看来就是调用 BeanFactoryPostProcessor. 如果对 spring bean 生命周期有些概念,其实不 debug 代码,我们也会猜到, @Service注解一定是通过BeanFactoryPostProcessor处理的,BeanFactoryPostProcessor这个接口就是用来管理bean 的定义的(BeanDefinition) , 那些需要实例化到容器中的 bean定义,一定是实例化之前先加载好的, 而在实例化之前能做这个事情的,应该只有BeanFactoryPostProcessor。 但是 debug 代码后我们会知道具体是那个BeanFactoryPostProcessor处理的,也就是ConfigurationClassPostProcessor的 postProcessBeanDefinitionRegistry()方法。 具体这个方法是怎么做找到要加载的类就不再具体分析了,大致看下,应该是扫描包路径,识别注解类型。
part 2: 接下来我们分析 dubbo provider HelloServiceImpl是如何实例化的
spring javabean 是什么时候实例化的本文的上半部分已经讨论过了,至于那些 bean 会被实例则依赖于DefaultListableBeanFactory类的 beanDefinitionNames属性。
所以我们首先讨论下 HelloServiceImpl 这个 dubbo server 的名称是什么时候被加入beanDefinitionNames的。根据本文上半部分,大概猜到dubbo的@Service注解应该也是通过某个 BeanFactoryPostProcessor处理的。 类似的在DefaultListableBeanFactory registerBeanDefinition方法打个有条件的断点:
然后观察调用堆栈:
很容易发现是ServiceAnnotationBeanPostProcessor这个类在处理dubbo 的@Service 接口。这个类也是一个 BeanFactoryPostProcessor的派生类,并且是 dubbo(com.alibaba.dubbo.config.spring.beans.factory.annotation) 包提供的。 因为我用@DubboComponentScan注解定义了 dubbo Server的扫描路径,所以从调用栈可以看到这个processor大致处理过程是扫描包路径,从 classPath 中寻找元数据,这里就不做过多分析了。
至此我们已经大概 dubbo server是什么时候由谁实例化的了,接下来我们分析下,dubbo server是什么时候向 zookeeper发布的。
part 3: dubbo provider 发布过程
先看下dubbo 的官方架构图:
从架构图上看,貌似是 ServiceConfig类的 export 方法执行的服务发布。我们在dubbo 源代码中实际加断点测试,也确实是这个方法做的发布(最终的发布代码是ServiceConfig doExportUrlsFor1Protocol()方法中的Exporter<?> exporter = protocol.export(wrapperInvoker);)。
OK, 接下来我们在 export方法中加个断点,看下调用堆栈:
可以看到发布服务的动作发生在applicationContext refresh完成的末尾:
在这个方法里,applicationContext 发布了一个事件:
dubbo 的 ServiceBean会监听这个事件:
ServiceBean继承自ServiceConfig, 这里的 export()就是 ServiceConfig 的 export()。而此时的ServiceConfig已经持有了具体的服务对象:
export()放回会根据 ServiceBean 持有的对象以及其它上下文来执行发布动作。
我们可以看出,其实 dubbo provider 会被封装成一个 ServiceBean, 而 ServiceBean 会在applicationContext即将 refresh完成的时候,监听时间负责发布任务。所以接下来的关键,是看ServiceBean如何实例化的。
part 3: ServiceBean实例化过程
ServiceBean的实例化代码与上两部分相同,同样依赖于DefaultListableBeanFactory类的 beanDefinitionNames属性。关键点依然在于ServiceBean name 是什么时候加入到beanDefinitionNames属性中的。
还是在同样的地方加类似的断点:
查看调用栈,发现也是ServiceAnnotationBeanPostProcessor注册的 bean. 具体方法如下:
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) { //省略代码 for (String packageToScan : packagesToScan) { Set<BeanDefinitionHolder> beanDefinitionHolders = scanner.doScan(packageToScan); if (CollectionUtils.isEmpty(beanDefinitionHolders)) { //省略 beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator); } if (!CollectionUtils.isEmpty(beanDefinitionHolders)) { for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { //此处是注册 serviceBean 的代码。 registerServiceBean(beanDefinitionHolder, registry); } //省略日志的代码 } //省略日志的代码 } }
可以看到注册 serviceBean 主要是依赖 beanDefinitionHolder这个参数,
这个参数是如何得到呢?就在上面几行:
Set<BeanDefinitionHolder> beanDefinitionHolders = scanner.doScan(packageToScan);
有心的同学发现,这个行代码正是注册 helloServiceImpl这个 dubbo provider 的 bean的 beanDefinition所执行的代码。
也就是说ServiceAnnotationBeanPostProcessor会先注册 dubbo provider的 BeanDefinition, 然后会返回这个BeanDefinition的BeanDefinitionHolder, 然后根据BeanDefinitionHolder注册这个 dubbo provider所依赖的 ServiceBean
至此,就完成了 dubbo provider的实例化和发布过程,通过阅读这段代码,会加深对 BeanFactoryPostProcessor 的理解,也会更加理解BeanFactoryPostProcessor和 BeanDefinition 之间是如何工作的。
============================================================
ServiceAnnotationBeanPostProcessor 这是一个 BeanFactoryPostProcessor的派生类,并且是 dubbo(com.alibaba.dubbo.config.spring.beans.factory.annotation) 包提供的, 负责管理 BeanDefinition
104 行 注册 dubbo service , 并返回beanDefinitionHolders
121 行 通过beanDefinitionHolders 注册 dubbo ServiceBean
- dubbo源码:provider发布service服务一
- dubbo源码:provider发布service服务三
- dubbo源码:provider发布service服务二
- dubbo源码解析(六) dubbo服务发布过程及本地暴露
- Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)
- DUBBO 源码解析(二) 服务的发布
- Dubbo 源码学习(四)初始化过程细节:解析服务
- 4.[dubbo源码解析]-[配置][详解]org.apche.dubbo.config.ServiceConfig-服务提供者暴露服务配置
- dubbo源码学习(四)初始化过程细节:解析服务
- Android源码解析之新进程中启动自定义服务过程(startService)的原理分析
- dubbo服务发布过程源码分析
- dubbo 源码学习笔记 (二) —— dubbo发布服务的过程
- JAVA SPI(Service Provider Interface)原理、设计及源码解析(其一)
- dubbo源码分析-服务发布
- Android 8.1 Camera2架构解析(1) CameraService和CameraProvider服务启动流程
- Dubbo源码解析 —— 服务暴露总结
- Dubbo源码解析 —— 逻辑层设计之服务降级
- dubbo源码分析二:服务发布
- Dubbo学习之路(四):Dubbo-SPI(service provider interface)源码解读
- 13.1 dubbo服务降级源码解析