天天看点

(三)DUBBO源码解析[email protected] provider发布服务过程

先祭概述

本文将从 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)是如何加载的

在构造方法中打一个断点:

(三)DUBBO源码解析[email protected] provider发布服务过程

找到初始化堆栈:

(三)DUBBO源码解析[email protected] provider发布服务过程

我们依次点进去,发现初始化入口在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的声明处,然后找到他所有的引用:

(三)DUBBO源码解析[email protected] provider发布服务过程

如上图,发现这个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的时候断住,看看这时候上游代码发生了什么。

(三)DUBBO源码解析[email protected] provider发布服务过程

执行到断点位置时,我们打开调用栈,依次观察下去,会发现两个重要的位置,就是下面圈红的位置:

(三)DUBBO源码解析[email protected] provider发布服务过程

第二个红框代码是:

// Invoke factory processors registered as beans in the context.
	invokeBeanFactoryPostProcessors(beanFactory);

           

第一个红框代码是:

总体看来就是调用 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方法打个有条件的断点:

(三)DUBBO源码解析[email protected] provider发布服务过程

然后观察调用堆栈:

(三)DUBBO源码解析[email protected] provider发布服务过程

很容易发现是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 的官方架构图:

(三)DUBBO源码解析[email protected] provider发布服务过程

从架构图上看,貌似是 ServiceConfig类的 export 方法执行的服务发布。我们在dubbo 源代码中实际加断点测试,也确实是这个方法做的发布(最终的发布代码是ServiceConfig doExportUrlsFor1Protocol()方法中的Exporter<?> exporter = protocol.export(wrapperInvoker);)。

OK, 接下来我们在 export方法中加个断点,看下调用堆栈:

(三)DUBBO源码解析[email protected] provider发布服务过程

可以看到发布服务的动作发生在applicationContext refresh完成的末尾:

(三)DUBBO源码解析[email protected] provider发布服务过程

在这个方法里,applicationContext 发布了一个事件:

(三)DUBBO源码解析[email protected] provider发布服务过程

dubbo 的 ServiceBean会监听这个事件:

(三)DUBBO源码解析[email protected] provider发布服务过程

ServiceBean继承自ServiceConfig, 这里的 export()就是 ServiceConfig 的 export()。而此时的ServiceConfig已经持有了具体的服务对象:

(三)DUBBO源码解析[email protected] provider发布服务过程

export()放回会根据 ServiceBean 持有的对象以及其它上下文来执行发布动作。

    我们可以看出,其实 dubbo provider 会被封装成一个 ServiceBean, 而 ServiceBean 会在applicationContext即将 refresh完成的时候,监听时间负责发布任务。所以接下来的关键,是看ServiceBean如何实例化的。

part 3: ServiceBean实例化过程

    ServiceBean的实例化代码与上两部分相同,同样依赖于DefaultListableBeanFactory类的 beanDefinitionNames属性。关键点依然在于ServiceBean name 是什么时候加入到beanDefinitionNames属性中的。

    还是在同样的地方加类似的断点:

(三)DUBBO源码解析[email protected] provider发布服务过程

查看调用栈,发现也是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这个参数,

这个参数是如何得到呢?就在上面几行:

有心的同学发现,这个行代码正是注册 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

继续阅读