Dubbo三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
JDK SPI 机制就存在以下一些问题:
Dubbo SPI 以 JDK SPI 为参考做出了改进设计,进行了性能优化【kv缓存】以及功能增强,Dubbo SPI 机制的出现解决了上述问题。除此之外,Dubbo 的 SPI 还支持自适应扩展以及 IOC 和 AOP 等高级特性。
//扩展接口类型
private final Class<?> type;
//存储了扩展实现类和扩展名的关系
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
//存储了扩展名和扩展实现类之间的关系
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
//存储了扩展实现类类名与实例对象之间的关系
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
//扩展接口对@SPI注解配置的value
private String cachedDefaultName;
Dubbo SPI 的配置做出了改进,在 Dubbo 中有三种不同的目录可以存放 SPI 配置,用途也不同,优先级由低到高如下所示:
其中,只有ServicesLoadingStrategy在其对应目录文件中是不支持kv形式的。
public class ExtensionLoader<T> {
// 利用JDK Spi机制提前初始化 LoadingStrategy的三个实现类
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
}
@SPI注解可以指定生效候选类的扩展名,否则扩展名选值为类名。候选类可以通过注解@Extension指定当前候选类的扩展名。
策略LoadingStrategy最终利用类加载器加载候选类资源的URL,得到候选类的二进制流,逐行读取,…,解析…返回。
目前Dubbo支持两种注册方式:应用级别以及接口级别的。
初学Dubbo在Web应用中的使用时经常用的注解为:@EnableDubbo
、@DubboReference
、@DubboService
三个,下面简单分析一下注解是如何发挥作用的。
@EnableDubbo
注解主要Import两个 ImportBeanDefinitionRegistrar 类型的子类:DubboComponentScanRegistrar、DubboConfigConfigurationRegistrar。但是DubboConfigConfigurationRegistrar可能是早期的版本,其功能完全可以被DubboComponentScanRegistrar取代。
DubboComponentScanRegistrar内部主要是在Spring IOC中增加类DubboSpringInitContext、接口级别的ModuleModel、应用级别的ApplicationModel。并且注册一些Dubbo框架需要使用到的核心bean,例如DubboDeployApplicationListener、DubboInfraBeanRegisterPostProcessor、DubboConfigApplicationListener等。
@DubboService
注解标识的目标类主要是被后置处理器ServiceAnnotationPostProcessor加载到Spring IOC对应的注册表中。
ServiceAnnotationPostProcessor是BeanDefinitionRegistryPostProcessor类型的后置处理器,不仅实现接口BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,同时实现了接口BeanFactoryPostProcessor的方法postProcessBeanFactory。
当然在DubboAutoConfiguration类中也初始化ServiceAnnotationPostProcessor,但是此处优先级明显低于BeanDefinitionRegistryPostProcessor。
public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor{
List<Class<? extends Annotation>> serviceAnnotationTypes =
// 如果普通类被以下三种注解标识都会被dubbo识别为目标类
asList(DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class
);
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
...
// 扫描指定路径下的bean
scanner.scan(packageToScan);
// 得到集合serviceAnnotationTypes元素注解存在的bean
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 解析@DubboService注解的相关属性,控制影响候选类实例化&初始化的流程
//其中候选类的BeanDefinition中class属性为ServiceBean
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
}
servicePackagesHolder.addScannedPackage(packageToScan);
}
}
}
DubboClassPathBeanDefinitionScanner扫描指定路径下dubbo涉及的bean。如果@EnableDubbo没有显式指定路径,则默认为启动类所在的包路径,此时意味着Dubbo需要遍历应用中所有类是否为候选类,所以显示指定路径可以提升应用启动速率。
Dubbo中目标接口的多个实现类必须通过version版本号区别。针对每个候选类在Spring IOC中存在两种形式,一种是beanName + Class属性(候选类真实Class类型),另一种是 beanName:version + Class属性为ServiceBean。最后一种形式也是后置处理器ServiceAnnotationPostProcessor类涉及的核心功能。
@DubboReference
注解是被后置处理器ReferenceAnnotationBeanPostProcessor解析的。当前后置处理器比较关注的注解如下所示分别为Reference、DubboReference。
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor{
public ReferenceAnnotationBeanPostProcessor() {
super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
}
简单概括为:遍历Spring IOC注册表中全部的类,如果类的字段或者方法上存在Reference|DubboReference等相关注解则生成该接口对应的BeanDefinition。但是BeanDefinition中Class属性为ReferenceBean
类信息。
ReferenceBean是FactoryBean类型的类,核心就是为了创建候选类的代理类,无论是JDK动态代理还是Cglib动态代理。但是在Dubbo中代理过程是参考Spring AOP 代理流程来完成的。
DubboConfigConfigurationRegistrar类型的后置处理器触发DubboSpringInitContext初始化,涉及核心流程:
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
DubboSpringInitializer.initialize(registry);
}
}
上述流程中Dubbo提供了一个扩展点:DubboSpringInitCustomizer。使用者可以提前修改DubboSpringInitContext内部相关属性。
ZookeeperRegistry