# Dubbo 解析

作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)


Dubbo是RPC框架, 提供了服务治理的功能, 在本项目案例中, Consumer是通过Dubbo调用Provider暴露的接口。

# Dubbo高级应用

  1. 集群容错

    Dubbo 提供了六种集群容错策略,每种策略的适用场景不同。集群容错机制可以保障服务调用的稳定性。以下是常用的几种:

    • Failover Cluster:失败自动切换,默认重试2次。适用于查询操作。
    • Failfast Cluster:服务调用失败后立即报错,适用于不允许失败的场景。
    • Failsafe Cluster:失败安全,出现异常时会忽略。适用于不重要的操作。
    • Failback Cluster:失败后自动恢复,适用于数据同步。
    • Forking Cluster:并行调用多个服务提供者,任意一个成功即返回。适用于对性能要求高的场景。
    • Broadcast Cluster:广播所有服务提供者,任意一个服务报错即表示调用失败。适用于需要广播的场景。

    配置示例

    @Service(cluster = "failfast")
    public class TestServiceImpl implements TestService {
    
        @Override
        public String test(String arg) {
            return "Hello World:"+arg;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 负载均衡

    Dubbo 提供了四种负载均衡策略,帮助将请求合理地分配到各个节点:

    • Random:随机策略,服务器性能较好的节点有更高的概率被选中。
    • RoundRobin:轮询策略,按比例分配请求。
    • LeastActive:最少活跃调用数策略,优先分配给负载较小的节点。
    • ConsistentHash:一致性哈希策略,保证请求参数相同的请求始终分配到同一节点。

    配置示例

    @Service(cluster = "failfast", loadbalance = "roundrobin")
    public class TestServiceImpl implements TestService {
        @Override
        public String test(String arg) {
            return "Hello World:" + arg;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  3. 服务降级

    当系统压力过大时,为了保证核心服务的稳定运行,Dubbo 提供了服务降级策略,可以通过Mock来实现。Mock策略在服务调用失败时,会返回预设的兜底数据。

    实现步骤

    1. 在consumer中实现降级策略

      public class MockTestServiceImpl implements TestService {
          @Override
          public String test(String arg) {
              return "Hello World:"+arg;
          }
      }
      
      1
      2
      3
      4
      5
      6
    2. @Reference注解中指定降级实现:

      @Reference(mock = "com.yym.dubbo.service.MockTestServiceImpl")
      private TestService testService;
      
      1
      2

# Dubbo核心源码解析

# Dubbo核心之SPI

# Java SPI扩展点

SPI(Service Provider Interface)是Java的一种扩展机制,旨在提供可插拔的功能。在Java中,像java.sql.Driver这样的接口是由数据库厂商实现的。JDK通过SPI机制,从classpath下自动查找实现类,以便为特定的数据库提供连接驱动。SPI的扩展遵循一些约定:扩展点文件需要放在resources/META-INF/services目录下,并且SPI机制会扫描这些文件并加载相应的扩展实现。

# Dubbo SPI实现

Dubbo也使用了类似Java SPI的机制,但它并没有直接使用JDK内置的SPI,而是对SPI进行了封装,提供了更多的灵活性和扩展性。Dubbo中的扩展点支持以下几种形式:

  • 自适应扩展点:根据调用时的上下文自动选择适当的扩展实现。
  • 指定名称的扩展点:通过指定的名称来加载特定的扩展实现。
  • 激活扩展点:通过某些条件激活特定的扩展实现。

通过以下方式,Dubbo实现了SPI的机制:

ExtensionLoader.getExtensionLoader(XXX.class).getAdaptiveExtension();
ExtensionLoader.getExtensionLoader(XXX.class).getExtension(name);
ExtensionLoader.getExtensionLoader(XXX.class).getActivateExtension(url, key);
1
2
3

Dubbo SPI的加载规则

  1. resources目录下,必须创建如META-INF/dubboMETA-INF/dubbo/internalMETA-INF/services等目录,并在对应目录下创建接口全路径命名的文件。Dubbo会从这些目录中加载扩展点。
  2. 文件内容和JDK内置SPI不一样, key是字符串, value是对应扩展点的实现, 可以按需加载指定的实现类

# 自定义Dubbo的扩展点

在一个依赖Dubbo的项目中,创建自定义的扩展点步骤如下:

  1. 创建扩展点接口,并用@SPI注解声明:

    @SPI
    public interface CustomDubboExtension {
        public String getName();
    }
    
    1
    2
    3
    4
  2. 实现扩展点接口

    public class CustomDubboExtensionImpl implements CustomDubboExtension {
        @Override
        public String getName() {
            return "加载自定义扩展点";
        }
    }
    
    1
    2
    3
    4
    5
    6
  3. resources/META-INF/dubbo目录下创建配置文件,文件名为CustomDubboExtension接口全路径,内容是扩展点的键值对:

    customextension=com.yym.dubbo.extension.impl.CustomDubboExtensionImpl
    
    1
  4. 在代码中获取扩展点实例

    @SpringBootApplication
    public class ProviderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProviderApplication.class, args);
            System.out.println("** provider start **");
    
            ExtensionLoader<CustomDubboExtension> extensionLoader = ExtensionLoader.getExtensionLoader(CustomDubboExtension.class);
            CustomDubboExtension customextension = extensionLoader.getExtension("customextension");
            System.out.println(customextension.getName());
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# Dubbo SPI 扩展点源码分析

基于上述案例代码分析Dubbo SPI 扩展点, 这段案例展示主要分为2段, 1获得ExtensionLoader, 2通过getExtension()获得指定名称的扩展点。

  1. ExtensionLoader.getExtensionLoader()

    该方法用于获取ExtensionLoader实例。其流程如下:

    • 首先从缓存中查找对应的ExtensionLoader实例。

    • 如果未命中缓存,则创建新的ExtensionLoader实例,并缓存起来。

      // 构造方法
      private ExtensionLoader(Class<?> type) {
          this.type = type;
          this.objectFactory = type == ExtensionFactory.class ? null : (ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();
      }
      
      // 获取 ExtensionLoader
      public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
              ...省略...
              ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
              if (loader == null) {
                  EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                  loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
              }
      
              return loader;
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18

      在构造ExtensionLoader时,初始化了一个objectFactory,用来后续扩展点实例的创建。

  2. getExtension()

    该方法用于根据扩展点的名称获取对应的扩展实现。其主要步骤如下:

    • 如果name"true",则返回默认扩展点。

    • 否则,通过createExtension(name)方法创建扩展点并缓存。

      public T getExtension(String name) {
          if (StringUtils.isEmpty(name)) {
              throw new IllegalArgumentException("Extension name == null");
          } else if ("true".equals(name)) {
              return this.getDefaultExtension();
          } else {
              // 创建一个Holder对象, 用于缓存实例
              Holder<Object> holder = this.getOrCreateHolder(name);
              Object instance = holder.get();
      		// 如果缓存中不存在, 创建一个实例
              if (instance == null) {
                  synchronized(holder) {
                      instance = holder.get();
                      if (instance == null) {
                          instance = this.createExtension(name);
                          holder.set(instance);
                      }
                  }
              }
      
              return instance;
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
    • createExtension()方法创建扩展点的实例,完成依赖注入,并可能进行包装。

      private T createExtension(String name) {
          // 获取一个扩展类
          Class<?> clazz = (Class)this.getExtensionClasses().get(name);
          if (clazz == null) {
              throw this.findException(name);
          } else {
              try {
                  // 缓存到EXTENSION_INSTANCES中
                  T instance = EXTENSION_INSTANCES.get(clazz);
                  if (instance == null) {
                      EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                      instance = EXTENSION_INSTANCES.get(clazz);
                  }
      			// 实现依赖注入
                  this.injectExtension(instance);
      			// 扩展类通过Wrapper进行包装
                  Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                  Class wrapperClass;
                  if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                      for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                          wrapperClass = (Class)var5.next();
                      }
                  }
      
                  return instance;
              } catch (Throwable var7) {
                  Throwable t = var7;
                  throw new IllegalStateException("Extension instance (name: " + name + ", class: " + this.type + ") couldn't be instantiated: " + t.getMessage(), t);
              }
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
  3. getExtensionClasses()

    返回一个Map<String, Class<?>>,其中key为配置文件中的扩展点名称,value为对应扩展点的实现类。通过loadExtensionClasses()方法加载扩展类。

    private Map<String, Class<?>> getExtensionClasses() {
    	// 从缓存中获取已经被加载的扩展类
        Map<String, Class<?>> classes = (Map)this.cachedClasses.get();
        if (classes == null) {
            synchronized(this.cachedClasses) {
                classes = (Map)this.cachedClasses.get();
                if (classes == null) {
    				// 如果未命中缓存, 则调用loadExtensionClasses加载扩展类
                    classes = this.loadExtensionClasses();
                    this.cachedClasses.set(classes);
                }
            }
        }
    
        return classes;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    • loadExtensionClasses()

      该方法从多个目录(如META-INF/dubbo/META-INF/services/等)加载扩展类,并返回一个Map<String, Class<?>>,保存扩展点的名称和实现类的对应关系。

      private Map<String, Class<?>> loadExtensionClasses() {
      	// 获得当前扩展接口的默认扩展对象, 并且缓存
          this.cacheDefaultExtensionName();
          // 加载指定文件目录下的配置文件
          Map<String, Class<?>> extensionClasses = new HashMap();
          this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName());
          this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName().replace("org.apache", "com.alibaba"));
          this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName());
          this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName().replace("org.apache", "com.alibaba"));
          this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName());
          this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName().replace("org.apache", "com.alibaba"));
          return extensionClasses;
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      • loadDirectory从指定目录下, 根据type全路径找到对应的文件, 解析内容后加载并保存到extensionClasses中

      • cacheDefaultExtensionName, 获得指定扩展接口的@SPI注解, 得到@SPI注解中的名字, 保存到cacheDefaultExtensionName属性中

        private void cacheDefaultExtensionName() {
            // 获取type类声明的SPI注解
            SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
            if (defaultAnnotation != null) {
                // 得到注解中的value值
                String value = defaultAnnotation.value();
                if ((value = value.trim()).length() > 0) {
                    String[] names = NAME_SEPARATOR.split(value);
                    if (names.length > 1) {
                        throw new IllegalStateException("More than 1 default extension name on extension " + this.type.getName() + ": " + Arrays.toString(names));
                    }
        
                    if (names.length == 1) {
                        this.cachedDefaultName = names[0];
                    }
                }
            }
        
        
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19

      }

     可以看出Dubbo的套路基本都是先缓存, 缓存未命中则创建再加入缓存, 流程并不复杂
    
    
    
    
    1
    2
    3
    4

# 自适应扩展点

自适应扩展点是根据上下文动态选择的扩展点。例如,Protocol接口的自适应扩展点可以根据调用时的参数来动态决定使用哪个实现。

通过getAdaptiveExtension()方法可以获取自适应扩展点:

public T getAdaptiveExtension() {
    // 从缓存中获取自适应扩展点实例
    Object instance = this.cachedAdaptiveInstance.get();
    if (instance == null) {
        if (this.createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " + this.createAdaptiveInstanceError.toString(), this.createAdaptiveInstanceError);
        }
		// 缓存未命中, 创建自适应扩展点并放入缓存
        synchronized(this.cachedAdaptiveInstance) {
            instance = this.cachedAdaptiveInstance.get();
            if (instance == null) {
                try {
                    instance = this.createAdaptiveExtension();
                    this.cachedAdaptiveInstance.set(instance);
                } catch (Throwable var5) {
                    Throwable t = var5;
                    this.createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }

    return instance;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

该方法会先检查缓存,如果缓存没有命中,就创建自适应扩展点并缓存。

自适应扩展点的创建过程涉及动态字节码生成,createAdaptiveExtensionClass()方法生成一个动态的自适应扩展类。

  • createAdaptiveExtension

    private T createAdaptiveExtension() {
        try {
            // 获取自适应扩展类, 并注入到spring容器
            return this.injectExtension(this.getAdaptiveExtensionClass().newInstance());
        } catch (Exception var2) {
            Exception e = var2;
            throw new IllegalStateException("Can't create adaptive extension " + this.type + ", cause: " + e.getMessage(), e);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    • getAdaptiveExtensionClass

      private Class<?> getAdaptiveExtensionClass() {
          this.getExtensionClasses();
          // 缓存中如果不存在ExtensionClass则创建
          return this.cachedAdaptiveClass != null ? this.cachedAdaptiveClass : (this.cachedAdaptiveClass = this.createAdaptiveExtensionClass());
      }
      
      1
      2
      3
      4
      5
      • createAdaptiveExtensionClass

        该类和扩展点有点不一样, 涉及动态字节码的生成和加载

        private Class<?> createAdaptiveExtensionClass() {
            // 一个动态拼接的类
            String code = (new AdaptiveClassCodeGenerator(this.type, this.cachedDefaultName)).generate();
            ClassLoader classLoader = findClassLoader();
            // 通过Compiler进行动态编译
            Compiler compiler = (Compiler)getExtensionLoader(Compiler.class).getAdaptiveExtension();
            return compiler.compile(code, classLoader);
        }
        
        1
        2
        3
        4
        5
        6
        7
        8

# Dubbo中的IoC 和 AOP源码机制分析

Dubbo框架中大量使用了 IoC(控制反转)与 AOP(面向切面编程)思想,这些机制在 Dubbo 的扩展点加载(SPI)过程中有明显体现,尤其集中在 ExtensionLoadercreateExtensioninjectExtension 方法中。下面将逐一分析这两种机制在源码中的体现。

# IoC

在Dubbo中,IoC的主要体现就是通过对象工厂注入依赖对象,而不是由扩展类主动创建依赖。

相关代码在 ExtensionLoader#createExtension() 方法中调用 injectExtension()

private T injectExtension(T instance) {
    try {
        if (this.objectFactory != null) {
			// 获取当前对象的所有public方法
            Method[] var11 = instance.getClass().getMethods();
            int var3 = var11.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                Method method = var11[var4];
                // 判断该方法是不是setter方法, 并且没有被DisableInject注解标记
                if (this.isSetter(method) && method.getAnnotation(DisableInject.class) == null) {                   // 获得扩展类中方法的参数类型
                    Class<?> pt = method.getParameterTypes()[0];
					// 只处理对象类型, 基本类型(int...)跳过
                    if (!ReflectUtils.isPrimitives(pt)) {
                        try {
                            String property = this.getSetterProperty(method);
                            // 根据 class 及 name, 从对象工厂中找对应的扩展点(并非new对象, 从容器获取)
                            Object object = this.objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception var9) {
                            Exception e = var9;
                            logger.error("Failed to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        }
    } catch (Exception var10) {
        Exception e = var10;
        logger.error(e.getMessage(), e);
    }

    return instance;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 关键点说明:

  • objectFactory:是 ExtensionFactory 类型,用于获取依赖对象,可能是Spring容器、SPI容器或其他实现。
  • IoC体现:当前扩展类所依赖的对象由外部注入,不需要自己创建,这就是典型的控制反转。

# AOP

AOP的本质是将横切逻辑与业务逻辑解耦,并通过“包装”方式在运行时进行增强。在 Dubbo 的 createExtension 方法中就有体现:

private T createExtension(String name) {
    ...
    // 实例化扩展类
    T instance = (T) clazz.getDeclaredConstructor().newInstance();
    
    // IoC: 执行依赖注入
    injectExtension(instance);

    // AOP: 包装扩展类(装饰器模式)
    Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
    if (CollectionUtils.isNotEmpty(wrapperClasses)) {
        for (Class<?> wrapperClass : wrapperClasses) {
            instance = injectExtension(
                wrapperClass.getConstructor(this.type).newInstance(instance)
            );
        }
    }

    return instance;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 关键点说明:

  • AOP实现方式:通过 Wrapper 类(如 ProtocolFilterWrapper、ProtocolListenerWrapper)包裹原始扩展实例,在调用接口方法前后执行增强逻辑。
  • 经典场景Protocol 接口在加载扩展时自动被 ProtocolFilterWrapperProtocolListenerWrapper 装饰。
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");

// 增强原始扩展实例
protocol = new ProtocolFilterWrapper(protocol);
protocol = new ProtocolListenerWrapper(protocol);
1
2
3
4
5

# Dubbo与Spring的整合机制(@Service/@Reference)

Dubbo 与 Spring 的整合核心是通过注解 + Spring 扩展点实现服务的注册与引用

# 1. @DubboComponentScan解析

作用是扫描指定包下的所有 Dubbo 注解(如 @Service),入口类为:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({DubboComponentScanRegistrar.class})
public @interface DubboComponentScan {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}
1
2
3
4
5
6
7
8
9
10
11
  • DubboComponentScanRegistrar 它实现了 ImportBeanDefinitionRegistrar 接口,在 Spring 启动时动态注册注解处理器:

    public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    	// 1. 获取扫描包
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        // 2. 注册@Service的注解处理器
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
        // 3. 注册@Reference的注解处理器
        registerReferenceAnnotationBeanPostProcessor(registry);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    • register***AnnotationBeanPostProcessor

      将 ServiceAnnotationBeanPostProcessor 与 ReferenceAnnotationBeanPostProcessor 注册到IoC容器

      private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
      
          BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
          builder.addConstructorArgValue(packagesToScan);
          builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
          AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
          BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
      
      }
      
      private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
      
          // Register @Reference Annotation Bean Processor
          registerInfrastructureBean(registry,
                  ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
      
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

      这样一看, DubboComponentScan注解只是将ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor注入IoC容器, 而前者用于解析@Service注解, 后者用于解析@Reference注解, 具体的解析需要查看AnnotationBeanPostProcessor的实现

# 2. ServiceAnnotationBeanPostProcessor 解析
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
}
1
2

从类的继承来看, 该类实现了一系列spring的扩展接口, BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor该类接口支持对beanDefinition信息的修改, 可以在beanDefinition加载完后实例化bean对象之前提供beanDefinition属性的修改机制。主要关注BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

BeanDefinitionRegistryPostProcessor

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
	// 早期的dubbo通过DubboBootstrapApplicationListener机制注册监听器, 监听spring的容器刷新事件, 统一导出ServiceBean
    registerBeans(registry, DubboBootstrapApplicationListener.class);
	// 格式调整
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        // 注册serviceBean
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

registerServiceBeans

  • DubboClassPathBeanDefinitionScanner 扫描指定路径下的类, 将符合条件的类装配到IoC容器
  • BeanNameGenerator 会通过一定的算法计算出需要装配Bean的name
  • addIncludeFilter 只扫描@Service注解修饰的类
  • 遍历指定的包, 通过findServiceBeanDefinitionHolders查找@Service注解修饰的类
  • 通过registerServiceBean完成Bean的注册
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 定义扫描对象
DubboClassPathBeanDefinitionScanner scanner =
        new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// beanName 解析器
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 添加过滤器 过滤@Service注解
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
// 查找@Service修饰的类
for (String packageToScan : packagesToScan) {

    scanner.scan(packageToScan);
    Set<BeanDefinitionHolder> beanDefinitionHolders =
            findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

    if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            // 注册bean
            registerServiceBean(beanDefinitionHolder, registry, scanner);
        }

    // ..............

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

registerServiceBean

  • 注意此处的ServiceBean是dubbo本身的ServiceBean
  • resolveClass 获得 BeanDefinitionHolder 中的bean
  • findServiceAnnotation 从beanClass中找到@Service注解
  • getAnnotationAttributes 获得注解中的属性, 如loadbalance cluster等
  • resolveServiceInterfaceClass 用于获得beanClass对应的接口定义
  • annotatedServiceBeanName bean名称
  • buildServiceBeanDefinition 构造dubbo ServiceBean对象, 每个dubbo服务的发布最终都会出现一个ServiceBean
  • 调用registerBeanDefinition将ServiceBean注入IoC
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) {
    // 获得 BeanDefinitionHolder 中的bean
    Class<?> beanClass = this.resolveClass(beanDefinitionHolder);
    // 从beanClass中找到@Service注解
    Annotation service = this.findServiceAnnotation(beanClass);
    // 获得注解中的属性, 如loadbalance cluster等
    AnnotationAttributes serviceAnnotationAttributes = AnnotationUtils.getAnnotationAttributes(service, false, false);
    // 用于获得beanClass对应的接口定义
    Class<?> interfaceClass = org.apache.dubbo.config.spring.util.AnnotationUtils.resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
    // 获取bean名称
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
    // 构造dubbo ServiceBean对象, 内部会用ServiceBean的BeanDefinition来包装service
    AbstractBeanDefinition serviceBeanDefinition = this.buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
    String beanName = this.generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
    if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
        // 将ServiceBean注入IoC
        registry.registerBeanDefinition(beanName, serviceBeanDefinition);
        // .....

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

实际上dubbo的serviceBean主要是发到网络上让其它服务进行调用, 这一步是怎么做的了?在dubbo 2.x版本, BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry#registerBeans 有这样一个方法, 该方法注册了一个DubboBootstrapApplicationListener事件监听spring容器的初始化完成事件, 监听到该事件后会启动一个网络监听, 从而实现服务发布。

dubbo3.x的版本对该方法进行了升级, 旧版本是由DubboBootstrapApplicationListener同一件进行ServiceBean的export(), 新版本每个ServiceBean实现了ContextRefreshedEvent, 也就是说每个ServiceBean自己export()自己;

结合实际代码来看这段代码

@Service(cluster = "failfast", loadbalance = "roundrobin ")
public class TestServiceImpl implements TestService {

    @Override
    public String test(String arg) {
        return "Hello World:" + arg;
    }
}
1
2
3
4
5
6
7
8

流程图(2.7.5以及新版本)

Spring 扫描到 @Service  (DubboScan只是扫描Dubbo的注解把它用ServiceBean包装注册到Spring容器)
       ↓
创建并注册 ServiceBean 实例(beanClass 为 ServiceBean)
       ↓
注册 DubboBootstrapApplicationListener
       ↓
Spring 容器启动完成(发布 ContextRefreshedEvent)
       ↓
DubboBootstrapApplicationListener 响应事件
       ↓
调用 DubboBootstrap.start()
       ↓
统一调用 ServiceBean.export()
       ↓
ref(实际服务实现类) 被暴露为 Dubbo 服务


新版本是这样进行导出
       ↓
Spring 容器发布 ContextRefreshedEvent
       ↓
每个 ServiceBean 实现了 ApplicationListener
       ↓
触发 onApplicationEvent()
       ↓
调用 this.export()
       ↓
ref(XxxServiceImpl)被单独暴露为 Dubbo 服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28