Davids原理探究:Dubbo配置解析原理

文章目录

    • Dubbo配置解析原理
      • 基于XML配置解析原理
        • schema模块说明
      • 基于注解配置解析原理
        • @EnableDubbo
        • ServiceAnnotationBeanPostProcessor的作用
        • ReferenceAnnotationBeanPostProcessor的作用
      • 配置初始化

Dubbo配置解析原理

关注可以查看更多粉丝专享blog~

上次本地编译的最新的Dubbo 2.7.8,跟着书看源码是基于2.6.5,从2.7.0版本开始dubbo的groupId已经变了。这里查看2.6.5的源码有两种方案

  1. 【推荐】下载Dubbo 2.6.5源码编译,编译流程和2.7.8一样。
  2. 直接用maven引入了alibaba dubbo 2.6.5的包,用IDEA查看源码学习,有个坑就是ali在dubbo里面有用自己的Spring support,需要单独依赖,不然有些类是没有的。

<groupId>com.alibabagroupId>


<groupId>org.apache.dubbogroupId>



 
 
 <dependency>
     <groupId>com.alibabagroupId>
     <artifactId>dubboartifactId>
     <version>2.6.5version>
 dependency>

 
 <dependency>
     <groupId>com.alibaba.springgroupId>
     <artifactId>spring-context-supportartifactId>
     <version>1.0.6version>
 dependency>

基于XML配置解析原理

什么是XSD(XML Schema Definition)?Dubbo框架直接集成了Spring的能力,利用了Spring配置文件扩展出自定义的解析方式dubbo.xsd。
dubbo.xsd文件用来约束使用XML配置时的标签和对应的属性,比如Dubbo中的标签等。Spring在解析到自定义的namespace标签时,会查找对应的spring.schemasspring.handlers文件,最终触发Dubbo的DubboNamespaceHandler类来进行初始化和解析。

#spring.schemas
http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd

# spring.handlers
http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler

schema模块说明

类型定义 功能概述
applicationType 配置应用级别的信息,比如应用名称,应用负责人和应用版本等
protocolType 配置服务提供者暴露的协议,Dubbo允许同时配置多个协议,但只能有一个协议默认暴露
registryType 配置注册中心的地址和协议,Dubbo也允许多个注册中心同时使用
providerType 配置服务提供方的全局配置,比如服务方设置了timeout,消费方会自动透传超时
consumerType 配置消费方全局配置,比如connections属性代表客户端会创建的TCP连接数,客户端全局配置会覆盖providerType透传的属性
serviceType 配置服务提供方接口范围信息,比如服务暴露的接口和具体实现类等
referenceType 配置消费方接口范围信息,比如引入的接口名称和是否范化调用标志等
moduleType 配置应用所属模块信息
monitorType 配置应用监控上报相关地址
methodType 配置方法级别参数,主要应用于
argumentType 配置应用参数方法等辅助信息,比如高级特性中异步参数回调索引的配置等
parameterType 选项参数配置,可以作为的字标签,方便添加自定义参数,会透传到框架的URL中

有了dubbo.xsd中约束的定义,以及如何扩展字段之后主要的解析逻辑在DubboBeanDefinitionParser#parse中完成。主要内容包含将标签解析成对应的Bean定义并注册到Spring上下文中,同时保证Spring容器中相同id的Bean不会覆盖。

  1. 首先判断容器中是否存在相同id的Bean。
    1. 如果不存在,则尝试获取XML配置标签的name和interface作为Bean的唯一id,如果协议标签没有指定name,则默认使用Dubbo。
    2. 如果存在,则每次解析会向Spring注册新的BeanDefinition,后续会追加属性。

DubboNamespaceHandler#init()

// public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement
@Override
public void init() {
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}

基于注解配置解析原理

注解处理逻辑主要包含3部分内容:

  1. 如果用户使用了配置文件,则框架按需生成对应的Bean。
  2. 要将所有使用Dubbo注解@Service的class提升为Bean。
  3. 要为使用@Reference注解的字段或方法注入代理对象。

@EnableDubbo

// @EnableDubbo 注解上加了@EnableDubboConfig、@DubboComponentScan
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
	...
}

// @EnableDubboConfig 注解上import了DubboConfigConfigurationSelector类
@Import(DubboConfigConfigurationSelector.class)
public @interface EnableDubboConfig {
	...
}

// @DubboComponentScan 注解上import了DubboComponentScanRegistrar类
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
	...
}

@DubboComponentScan

  1. 激活DubboComponentScanRegistrar
  2. 生成ServiceAnnotationBeanPostProcessor处理器
  3. 生成ReferenceAnnotationBeanPostProcessor处理器。
// public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar
// DubboComponentScanRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	// 获取扫描包路径
    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
	// 激活ServiceAnnotationBeanPostProcessor
    registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
	// 激活ReferenceAnnotationBeanPostProcessor
    registerReferenceAnnotationBeanPostProcessor(registry);

}

ServiceAnnotationBeanPostProcessor的作用

// ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
	// 获取用户注解配置的包扫描(@EnableDubbo 的value值,没有则默认注解类的包路径)
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
	// 触发ServiceBean的定义和注入
    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }
}

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
    DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    scanner.setBeanNameGenerator(beanNameGenerator);
	// 扫描Dubbo的注解@Service,不会扫描Spring的@Service注解
    scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
    for (String packageToScan : packagesToScan) {
        // 将@Service作为不同的Bean注入容器
        scanner.scan(packageToScan);
        // 对扫描的服务创建Beandefinitionholder,用于生成ServiceBean定义
        Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            	// 注册ServiceBean定义与数据绑定
                registerServiceBean(beanDefinitionHolder, registry, scanner);
            }
            
            ...
            
        }
    }

}

ReferenceAnnotationBeanPostProcessor的作用

Davids原理探究:Dubbo配置解析原理_第1张图片

public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor<Reference>
        implements ApplicationContextAware, ApplicationListener

// 父类 AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
	// 查找Bean所有标注了@Reference的字段和方法
    InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
    try {
    	// 对字段、方法进行反射绑定
        metadata.inject(bean, beanName, pvs);
    } catch (BeanCreationException ex) {
        throw ex;
    } catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
                + " dependencies is failed", ex);
    }
    return pvs;
}

private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // 返回到类名作为缓存键,以便向后兼容自定义调用者
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // 首先对并发映射进行快速检查,使用最小的锁定
    AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                try {
                	// 处理注解字段和方法
                    metadata = buildAnnotatedMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                } catch (NoClassDefFoundError err) {
                    throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
                            "] for annotation metadata: could not find class that it depends on", err);
                }
            }
        }
    }
    return metadata;
}

// 处理带@Reference的字段和方法
private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
	// 处理带@Reference的字段
  	Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
  	// 处理带@Reference的方法
    Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
    // 注入容器
    return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}

// 处理带@Reference的字段
private List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
    final List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement>();
    ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
    	// 遍历查找所有带@Reference的非静态字段添加到List最后返回
        @Override
        public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
            A annotation = getAnnotation(field, getAnnotationType());
            if (annotation != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("@" + getAnnotationType().getName() + " is not supported on static fields: " + field);
                    }
                    return;
                }
                elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement(field, annotation));
            }
        }
    });
    return elements;
}

// 处理带@Reference的方法
private List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {
    final List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement>();
    ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
    	// 遍历查找所有带@Reference的非静态方法添加到List最后返回
        @Override
        public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
            Method bridgedMethod = findBridgedMethod(method);
            if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            A annotation = findAnnotation(bridgedMethod, getAnnotationType());
            if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
                if (Modifier.isStatic(method.getModifiers())) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("@" + getAnnotationType().getSimpleName() + " annotation is not supported on static methods: " + method);
                    }
                    return;
                }
                if (method.getParameterTypes().length == 0) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("@" + getAnnotationType().getSimpleName() + " annotation should only be used on methods with parameters: " +
                                method);
                    }
                }
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
                elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement(method, pd, annotation));
            }
        }
    });
    return elements;

}

配置初始化

不管是在服务暴露还是在服务消费场景下,Dubbo框架都会根据优先级对配置信息做聚合处理,目前默认覆盖策略主要遵循以下三点规则:

  1. -D传递给JVM参数优先级最高,比如-Ddubbo.protocol.port=20880
  2. 代码或者XML配置优先级次高,如:Spring中XML文件制定
  3. 配置文件优先级最低,如:dubbo.properties文件指定dubbo.protocol.port=20880
    一般推荐使用dubbo.properties作为默认值,只有JVM没有指定参数,并且XML没有匹配时,dubbo.properties才会生效,通常用语共享公共配置,如应用名称等。

Dubbo的配置也会受到provider的影响,这个属于运行期属性值影响,同样遵循一下两点规则:

  1. 如果只有provider端指定配置,则会自动透传到客户端(如:timeout)
  2. 如果客户端也有相应配置,则服务端配置会被覆盖(如:timeout)
    运行时属性随着框架特性可以动态添加,不允许透传的属性会在ClusterUtils#mergeUrl中进行特殊处理。
// ClusterUtils#mergeUrl
public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
   	Map<String, String> map = new HashMap<String, String>();
    Map<String, String> remoteMap = remoteUrl.getParameters();
    if (remoteMap != null && remoteMap.size() > 0) {
        map.putAll(remoteMap);

        // 移除禁止透传的参数
        map.remove(Constants.THREAD_NAME_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREAD_NAME_KEY);

        map.remove(Constants.THREADPOOL_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADPOOL_KEY);

        map.remove(Constants.CORE_THREADS_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.CORE_THREADS_KEY);

        map.remove(Constants.THREADS_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADS_KEY);

        map.remove(Constants.QUEUES_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.QUEUES_KEY);

        map.remove(Constants.ALIVE_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.ALIVE_KEY);

        map.remove(Constants.TRANSPORTER_KEY);
        map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.TRANSPORTER_KEY);
    }
    if (localMap != null && localMap.size() > 0) {
        map.putAll(localMap);
    }
    if (remoteMap != null && remoteMap.size() > 0) {
        // 使用从provider 传递的version 
        String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);
        if (dubbo != null && dubbo.length() > 0) {
            map.put(Constants.DUBBO_VERSION_KEY, dubbo);
        }
        String version = remoteMap.get(Constants.VERSION_KEY);
        if (version != null && version.length() > 0) {
            map.put(Constants.VERSION_KEY, version);
        }
        String group = remoteMap.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            map.put(Constants.GROUP_KEY, group);
        }
        String methods = remoteMap.get(Constants.METHODS_KEY);
        if (methods != null && methods.length() > 0) {
            map.put(Constants.METHODS_KEY, methods);
        }
        // 保留provider url的时间戳
        String remoteTimestamp = remoteMap.get(Constants.TIMESTAMP_KEY);
        if (remoteTimestamp != null && remoteTimestamp.length() > 0) {
            map.put(Constants.REMOTE_TIMESTAMP_KEY, remoteMap.get(Constants.TIMESTAMP_KEY));
        }
        // 在Provider和Consumer上组合过滤器和侦听器
        String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
        String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
        if (remoteFilter != null && remoteFilter.length() > 0
                && localFilter != null && localFilter.length() > 0) {
            localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
        }
        String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);
        String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);
        if (remoteListener != null && remoteListener.length() > 0
                && localListener != null && localListener.length() > 0) {
            localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
        }
    }
    return remoteUrl.clearParameters().addParameters(map);
}

相关文章:
Davids原理探究:Dubbo源码编译(2.7.8)
Davids原理探究:Dubbo SPI和Java SPI实现原理
Davids原理探究:Dubbo注册中心(ZooKeeper、Redis)实现原理
Davids原理探究:Dubbo配置解析原理
Davids原理探究:Dubbo服务暴露原理
Davids原理探究:Dubbo服务消费原理
Davids原理探究:Dubbo优雅停机原理解析
Davids原理探究:Dubbo调用流程图
Davids原理探究:Dubbo路由实现原理
Davids原理探究:Dubbo负载均衡实现原理
Davids原理探究:Dubbo过滤器原理

你可能感兴趣的:(Dubbo,微服务,Java)