您的位置:首页 > 其它

(三)DUBBO源码解析----@Service provider发布服务过程

2019-01-30 21:25 585 查看

先祭概述
本文将从 spring如何加载实例入手:

  1. 先分析如何加载spring普通实例
  2. 再分析如何加载 dubbo 服务
  3. 然后分析加载 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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: