主頁 >  其他 > Dubbo2.7原始碼詳解

Dubbo2.7原始碼詳解

2022-10-18 07:28:24 其他

Spring與Dubbo整合原理與原始碼分析

  【1】注解@EnableDubbo

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig   // @EnableDubboConfig注解用來將properties檔案中的配置項轉化為對應的Bean
@DubboComponentScan  // @DubboComponentScan注解用來掃描服務提供者和參考者(@Service與@Reference)
public @interface EnableDubbo {

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default true;

}

 

  【2】注解@EnableDubboConfig

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {

    boolean multiple() default true;
}

    1)DubboConfigConfigurationRegistrar類的作用

//因為實作了ImportBeanDefinitionRegistrar介面,spring容器就會實體化該類,并且呼叫其registerBeanDefinitions方法;
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //執行DubboConfigConfigurationRegistrar;

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        boolean multiple = attributes.getBoolean("multiple"); //默認值是true

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);

        if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
    }
}

 

    2)registerBeans做了什么

public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {
    if (ObjectUtils.isEmpty(annotatedClasses)) {
        return;
    }
    ...
    AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
    ...
    // 利用Spring中的AnnotatedBeanDefinitionReader來決議annotatedClasses
    // 會決議該類上的注解,然后進行處理
    reader.register(annotatedClasses);

}

 

    3)DubboConfigConfiguration類展示

public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableDubboConfigBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.class)
    })
    public static class Single {}

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {}
}

 

    4)那么必然又會決議到@EnableDubboConfigBindings注解

//又是利用了實作了ImportBeanDefinitionRegistrar介面,在實體化該類會呼叫其registerBeanDefinitions方法;
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //執行DubboConfigBindingsRegistrar
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));

        // 拿到多個@EnableDubboConfigBinding注解
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
        //將環境變數注入
        registrar.setEnvironment(environment);

        for (AnnotationAttributes element : annotationAttributes) {
            // 逐個決議@EnableDubboConfigBinding注解,比如@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)
            registrar.registerBeanDefinitions(element, registry);
        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment) environment;
    }

}

 

    5)registrar.registerBeanDefinitions方法的呼叫情況

public class DubboConfigBindingRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private final Log log = LogFactory.getLog(getClass());

    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //執行DubboConfigBindingRegistrar

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBinding.class.getName()));

        registerBeanDefinitions(attributes, registry);

    }

    protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

        // prefix = "dubbo.application"
        String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));

        // type = ApplicationConfig.class
        Class<? extends AbstractConfig> configClass = attributes.getClass("type");

        boolean multiple = attributes.getBoolean("multiple");
        //針對配置分別進行注冊成Bean物件,方法1
        registerDubboConfigBeans(prefix, configClass, multiple, registry);

    }

    //方法1,因為Single和Multiple都是走同一套邏輯,采用引數boolean multiple區分
    private void registerDubboConfigBeans(String prefix, Class<? extends AbstractConfig> configClass, boolean multiple, BeanDefinitionRegistry registry) {

        // 從properties檔案中根據前綴拿對應的配置項,比如根據dubbo.application前綴,
        // 就可以拿到如下配置:
        // dubbo.application.name=dubbo-demo-provider-application
        // dubbo.application.logger=log4j
        Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);

        // 如果沒有相關的配置項,則不需要注冊BeanDefinition
        if (CollectionUtils.isEmpty(properties)) {
            if (log.isDebugEnabled()) {
                log.debug(...);
            }
            return;
        }

        // 根據配置項生成beanNames,為什么會有多個?
        // 普通情況一個dubbo.application前綴對應一個ApplicationConfig型別的Bean
        // 特殊情況下(配置兩種協議),比如dubbo.protocols對應了:
        //        dubbo.protocols.p1.name=dubbo
        //        dubbo.protocols.p1.port=20880
        //        dubbo.protocols.p1.host=0.0.0.0

        //        dubbo.protocols.p2.name=http
        //        dubbo.protocols.p2.port=8082
        //        dubbo.protocols.p2.host=0.0.0.0
        // 那么就需要對應兩個ProtocolConfig型別的Bean,那么就需要兩個beanName:p1和p2

        // 這里就是multiple為true或false的區別,名字的區別,根據multiple用來判斷是否從配置項中獲取beanName
        // 如果multiple為false,則看有沒有配置id屬性,如果沒有配置則自動生成一個beanName.
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) : Collections.singleton(resolveSingleBeanName(properties, configClass, registry));

        for (String beanName : beanNames) {
            // 為每個beanName,注冊一個空的BeanDefinition,方法2
            registerDubboConfigBean(beanName, configClass, registry);

            // 為每個bean注冊一個DubboConfigBindingBeanPostProcessor的Bean后置處理器,方法3
            //這里存在的問題就是對應每一種配置都會產生對應的BeanPostProcessor,最多好像也就是10種左右
            //但其實一個就可以做的任務,拓展成多個貌似不太合理,結合處理邏輯都是同一套就很尷尬
            registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
        }

        // 注冊一個NamePropertyDefaultValueDubboConfigBeanCustomizer的bean
        registerDubboConfigBeanCustomizers(registry);

    }

    //方法2,為對應的配置生成一個beanDefinition,并注入到容器
    private void registerDubboConfigBean(String beanName, Class<? extends AbstractConfig> configClass,BeanDefinitionRegistry registry) {

        BeanDefinitionBuilder builder = rootBeanDefinition(configClass);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        registry.registerBeanDefinition(beanName, beanDefinition);   // ApplicatinoConfig物件

        if (log.isInfoEnabled()) {
            log.info("...); //日志記錄
        }

    }

    //方法3
    private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple,BeanDefinitionRegistry registry) {

        // 注冊一個DubboConfigBindingBeanPostProcessor的Bean
        // 每個XxConfig的Bean對應一個DubboConfigBindingBeanPostProcessor的Bean
        // 比如,一個ApplicationConfig對應一個DubboConfigBindingBeanPostProcessor,
        // 一個ProtocolConfig也會對應一個DubboConfigBindingBeanPostProcessor
        // 在構造DubboConfigBindingBeanPostProcessor的時候會指定構造方法的值,這樣就可以區別開來了

        Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class;

        BeanDefinitionBuilder builder = rootBeanDefinition(processorClass);

        // 真實的前綴,比如dubbo.registries.r2
        String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix;

        // 添加兩個構造方法引數值,所以會呼叫DubboConfigBindingBeanPostProcessor的兩個引數的構造方法
        builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        registerWithGeneratedName(beanDefinition, registry);

        if (log.isInfoEnabled()) {
            log.info(...);
        }

    }

    private void registerDubboConfigBeanCustomizers(BeanDefinitionRegistry registry) {
        registerInfrastructureBean(registry, BEAN_NAME, NamePropertyDefaultValueDubboConfigBeanCustomizer.class);
    }

    @Override
    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment) environment;

    }

    private Set<String> resolveMultipleBeanNames(Map<String, Object> properties) {
        Set<String> beanNames = new LinkedHashSet<String>();

        // 比如dubbo.protocols.p1.name=dubbo的propertyName為p1.name
        for (String propertyName : properties.keySet()) {

            // propertyName為p1.name
            int index = propertyName.indexOf(".");
            if (index > 0) {
                // 截取beanName名字為p1
                String beanName = propertyName.substring(0, index);
                beanNames.add(beanName);
            }
        }
        return beanNames;

    }

    private String resolveSingleBeanName(Map<String, Object> properties, Class<? extends AbstractConfig> configClass,BeanDefinitionRegistry registry) {

        // 配置了dubbo.application.id=appl,那么appl就是beanName
        String beanName = (String) properties.get("id");
        // 如果beanName為null,則會進入if分支,由spring自動生成一個beanName,比如org.apache.dubbo.config.ApplicationConfig#0
        if (!StringUtils.hasText(beanName)) {
            BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
            beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry);
        }

        return beanName;

    }

}

 

    6)單個DubboConfigBindingBeanPostProcessor的展示(刪減掉部分不怎么用到的)

public class DubboConfigBindingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, InitializingBean, BeanDefinitionRegistryPostProcessor {

    private final String prefix;

    private final String beanName;

    private DubboConfigBinder dubboConfigBinder;
    ....
    private List<DubboConfigBeanCustomizer> configBeanCustomizers = Collections.emptyList();
  ....
  
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        // 每個XxConfig對應一個BeanPostProcessor,所以每個DubboConfigBindingBeanPostProcessor只處理對應的beanName
        if (this.beanName.equals(beanName) && bean instanceof AbstractConfig) {

            AbstractConfig dubboConfig = (AbstractConfig) bean;
            // 從properties檔案中獲取值,并設定到dubboConfig物件中
            bind(prefix, dubboConfig);

            // 設定dubboConfig物件的name屬性,設定為beanName
            customize(beanName, dubboConfig);

        }

        return bean;

    }

    private void bind(String prefix, AbstractConfig dubboConfig) {
        dubboConfigBinder.bind(prefix, dubboConfig);

        if (log.isInfoEnabled()) {
            log.info(...);
        }
    }

    private void customize(String beanName, AbstractConfig dubboConfig) {
        for (DubboConfigBeanCustomizer customizer : configBeanCustomizers) {
            customizer.customize(beanName, dubboConfig);
        }
    }

   ...
    @Override
    public void afterPropertiesSet() throws Exception {
        initDubboConfigBinder();        // 創建DefaultDubboConfigBinder
        initConfigBeanCustomizers();
    }

    private void initDubboConfigBinder() {
        if (dubboConfigBinder == null) {
            try {
                // 先從Spring容器中獲取DubboConfigBinder,默認獲取不到
                dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
            } catch (BeansException ignored) {
                if (log.isDebugEnabled()) {
                    log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
                }
                // Use Default implementation
                // 生成一個默認的
                dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
            }
        }

        dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields);
        dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);

    }

    private void initConfigBeanCustomizers() {
        // 得到之前創建了的NamePropertyDefaultValueDubboConfigBeanCustomizer
        Collection<DubboConfigBeanCustomizer> configBeanCustomizers = beansOfTypeIncludingAncestors(applicationContext, DubboConfigBeanCustomizer.class).values();

        this.configBeanCustomizers = new ArrayList<>(configBeanCustomizers);
        AnnotationAwareOrderComparator.sort(this.configBeanCustomizers);
    }
...
}

 

  【3】注解@DubboComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

}

 

    1)匯入的DubboComponentScanRegistrar類做了什么

/又是利用了實作了ImportBeanDefinitionRegistrar介面,在實體化該類會呼叫其registerBeanDefinitions方法;
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //執行DubboComponentScanRegistrar

        // 拿到DubboComponentScan注解所定義的包路徑,掃描該package下的類,識別這些類上
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        // 注冊ServiceAnnotationBeanPostProcessor一個Bean
        // 實作了BeanDefinitionRegistryPostProcessor介面,所以在Spring啟動時會呼叫postProcessBeanDefinitionRegistry方法
        // 該方法會進行掃描,掃描@Service注解了的類,然后生成BeanDefinition(會生成兩個,一個普通的bean,一個ServiceBean),后續的Spring周期中會生成Bean
        // 在ServiceBean中會監聽ContextRefreshedEvent事件,一旦Spring啟動完后,就會進行服務匯出
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

        // 注冊ReferenceAnnotationBeanPostProcessor
        // 實作了AnnotationInjectedBeanPostProcessor介面,繼而實作了InstantiationAwareBeanPostProcessorAdapter介面
        // 所以Spring在啟動時,在對屬性進行注入時會呼叫AnnotationInjectedBeanPostProcessor介面中的postProcessPropertyValues方法
        // 在這個程序中會按照@Refrence注解的資訊去生成一個RefrenceBean物件
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

    //核心方法1,注冊一個對@Service注解處理的 BeanDefinitionRegistryPostProcessor
    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        // 生成一個RootBeanDefinition,對應的beanClass為ServiceAnnotationBeanPostProcessor.class
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        // 將包路徑作為在構造ServiceAnnotationBeanPostProcessor時呼叫構造方法時的傳入引數
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }

    //核心方法2,注冊一個對屬性賦值處理的AnnotationInjectedBeanPostProcessor且帶有ApplicationListener事件監聽功能
    private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {

        // Register @Reference Annotation Bean Processor
        // 注冊一個ReferenceAnnotationBeanPostProcessor做為bean,ReferenceAnnotationBeanPostProcessor是一個BeanPostProcessor
        BeanRegistrar.registerInfrastructureBean(registry,ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);

    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }

}

 

  【4】掃描@Service注解,并且進行處理

    匯總說明:實際上便是通過處理器掃描@Service注解的類,生成兩個Bean【類對應的普通Bean,與Dubbo中要用到的ServiceBean】

    其中ServiceBean,是先根據注解上的資訊填充對應的屬性,后采用環境變數中獲取配置的屬性,來完成屬性填充,

public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,ResourceLoaderAware, BeanClassLoaderAware {

    ...
    //核心方法1
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            // 掃描包,進行Bean注冊,核心方法2呼叫
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }


    //核心方法2
    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        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));

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            // 掃描Dubbo自定義的@Service注解
            scanner.scan(packageToScan);

            // 查找被@Service注解的類的BeanDefinition(無論這個類有沒有被@ComponentScan注解標注了)
            Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                // 掃描到BeanDefinition開始處理它,核心方法3的呼叫
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) { logger.info(b...); }
            } else {
                if (logger.isWarnEnabled()) { logger.warn(...); }
            }

        }

    }

    //核心方法3
    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) {
        // 處理掃描到的每一個BeanDefinition
        // 1. 得到@Service注解上所配置的引數
        // 2. 根據每一個BeanDefinition會再額外的生成一個ServiceBean
        // 3. 對于每一個被@Service注解的類(服務的實作類),會生成兩個Bean,一個服務實作類對應的Bean(普通Bean,和@Component一樣),一個ServiceBean(Dubbo中要用到的Bean,因為在ServiceBean中包括了很的Config)

        // 具體的服務實作類
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        // @Service可以對服務進行各種配置
        Annotation service = findServiceAnnotation(beanClass);

        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

        // 服務實作類對應的介面
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
        // 服務實作類對應的bean的名字,比如:demoServiceImpl
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

        // 生成一個ServiceBean,核心方法4的呼叫
        AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name   ServiceBean表示服務,我們要使用一個服務應該拿ServiceBean
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean

            // 把ServiceBean注冊進去,對應的beanName為ServiceBean:org.apache.dubbo.demo.DemoService
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

            if (logger.isInfoEnabled()) { logger.info(..); }
        } else {
            if (logger.isWarnEnabled()) { logger.warn(...); }
        }
    }

...

    //核心方法4
    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,AnnotationAttributes serviceAnnotationAttributes,Class<?> interfaceClass,String annotatedServiceBeanName) {
        // 生成一個ServiceBean對應的BeanDefinition
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");

        // 把serviceAnnotation中的引數值賦值給ServiceBean的屬性
        // 如:@Service(test = "test") 
        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));

        // References "ref" property to annotated-@Service Bean
        // 如:@Service(protocol = "P1"),這種就是要根據對應的值找到對應的P1的config物件里面的值
        // ref屬性賦值為另外一個bean, 對應的就是被@Service注解的服務實作類對應的bean
        addPropertyReference(builder, "ref", annotatedServiceBeanName);

        // Set interface
        builder.addPropertyValue("interface", interfaceClass.getName());
        // Convert parameters into map
        builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));

        // 配置了methods屬性,則給ServiceBean對應的methods屬性賦值
        // Add methods parameters
        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
        if (!methodConfigs.isEmpty()) {
            builder.addPropertyValue("methods", methodConfigs);
        }

        /**
         * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
         */
        String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
        if (StringUtils.hasText(providerConfigBeanName)) {
            addPropertyReference(builder, "provider", providerConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
         */
        String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
        if (StringUtils.hasText(monitorConfigBeanName)) {
            addPropertyReference(builder, "monitor", monitorConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
         */
        String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
        if (StringUtils.hasText(applicationConfigBeanName)) {
            addPropertyReference(builder, "application", applicationConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
         */
        String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
        if (StringUtils.hasText(moduleConfigBeanName)) {
            addPropertyReference(builder, "module", moduleConfigBeanName);
        }


        /**
         * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
         * 獲取注解上配置的注冊中心的beanName
         */
        String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");

        List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);

        if (!registryRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("registries", registryRuntimeBeanReferences);
        }

        /**
         * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
         */
        String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");

        List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);

        if (!protocolRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
        }

        return builder.getBeanDefinition();

    }
....
}

 

  【5】掃描@Reference注解,并且進行處理

    1)ReferenceAnnotationBeanPostProcessor類會被呼叫是基于繼承關系

//class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor
//abstract class AnnotationInjectedBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter 
//InstantiationAwareBeanPostProcessorAdapter類便是屬性注入時候會呼叫的
//呼叫AnnotationInjectedBeanPostProcessor抽象類的postProcessPropertyValues方法
@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

    // 尋找需要注入的屬性(被@Reference標注的Field)
    InjectionMetadata metadata =https://www.cnblogs.com/chafry/archive/2022/10/17/ 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().getSimpleName()
                + " dependencies is failed", ex);
    }
    return pvs;
}

//最終走回到ReferenceAnnotationBeanPostProcessor類的doGetInjectedBean方法

 

    2)ReferenceAnnotationBeanPostProcessor中的方法

public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor implements ApplicationContextAware, ApplicationListener {
...
    // 該方法得到的物件會賦值給@ReferenceBean注解的屬性
    @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,InjectionMetadata.InjectedElement injectedElement) throws Exception {

        // 得到引入服務的beanName
        // attributes里存的是@Reference注解中的所配置的屬性與值
        // injectedType表示引入的是哪個服務介面
        // referencedBeanName的值為  ServiceBean:org.apache.dubbo.demo.DemoService  表示得到該服務Bean的beanName
        // referencedBeanName表示 我現在要參考的這個服務,它匯出時對應的ServiceBean的beanName是什么,可以用來判斷現在我參考的這個服務是不是我自己匯出的
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);


        // @Reference(methods=[Lorg.apache.dubbo.config.annotation.Method;@39b43d60) org.apache.dubbo.demo.DemoService
        // 我要生成一個RefrenceBean,對應的beanName, 根據@Reference注解來標識不同
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);

        // 生成一個ReferenceBean物件,方法1
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

        // 把referenceBean添加到Spring容器中去,方法2
        registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);

        cacheInjectedReferenceBean(referenceBean, injectedElement);

        // 創建一個代理物件,Service中的屬性被注入的就是這個代理物件
        // 內部會呼叫referenceBean.get(); ,核心方法1
        return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
    }

    //方法1
    private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes, Class<?> referencedType) throws Exception {

        ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);

        if (referenceBean == null) {

            // 生成了一個ReferenceBean物件,attributes是@Reference注解的引數值
            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                    .create(attributes, applicationContext)
                    .interfaceClass(referencedType);
            referenceBean = beanBuilder.build();

            referenceBeanCache.put(referenceBeanName, referenceBean);
        } else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
            throw new IllegalArgumentException(...);
        }
        return referenceBean;
    }

    //方法2
    private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean, AnnotationAttributes attributes, Class<?> interfaceClass) {

        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // @Reference(parameters=[Ljava.lang.String;@72ef8d15) org.apache.dubbo.demo.DemoService
        // ReferenceBean的beanName,注意這個beanName,它是直接取的@Reference的全資訊
        // 所以,就算參考的是同一個服務,如果@Reference注解上的資訊不同,那么就會生成不同的ReferenceBean
        String beanName = getReferenceBeanName(attributes, interfaceClass);

        // 要引入的服務就是本地提供的一個服務
        if (existsServiceBean(referencedBeanName)) { // If @Service bean is local one
            /**
             * Get  the @Service's BeanDefinition from {@link BeanFactory}
             * Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
             */
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
            RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref"); // ServiceBean --- ref
            // The name of bean annotated @Service
            String serviceBeanName = runtimeBeanReference.getBeanName();
            // register Alias rather than a new bean name, in order to reduce duplicated beans
            // 如果是本地提供的一個服務,那么就@Reference(parameters=[Ljava.lang.String;@72ef8d15) org.apache.dubbo.demo.DemoService
            // 的別名是demoService,不需要是ServiceBean的名字
            beanFactory.registerAlias(serviceBeanName, beanName);
        } else { // Remote @Service Bean
            if (!beanFactory.containsBean(beanName)) {
                beanFactory.registerSingleton(beanName, referenceBean);
            }
        }
    }

    //核心方法1
    //這里面其實有點繞,因為@Reference其實也相當于做了@Autowired的作業
    //能在本地找到,如果不代理的話其實相當于@Autowired注入屬性(不會走Dubbo的邏輯),所以包裝成代理,讓它也走Dubbo的邏輯
    private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<?> serviceInterfaceType) {
        //這個其實是判斷本地有沒有
        if (existsServiceBean(referencedBeanName)) { // If the local @Service Bean exists, build a proxy of ReferenceBean
            //進行代理,讓它走Dubbo的邏輯
            return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType}, wrapInvocationHandler(referenceBeanName, referenceBean));
        } else {                                    // ReferenceBean should be initialized and get immediately
            // 重點,服務引入的地方
            return referenceBean.get();
        }
    }

...
}

 

  【6】圖示:

     

 

Dubbo服務匯出

  【0】服務匯出要做的幾件事情:

1. 確定服務的引數
2. 確定服務支持的協議
3. 構造服務最終的URL
4. 將服務URL注冊到注冊中心去
5. 根據服務支持的不同協議,啟動不同的Server,用來接收和處理請求
6. 因為Dubbo支持動態配置服務引數,所以服務匯出時還需要系結一個監聽器Listener來監聽服務的引數是否有修改,如果發現有修改,則需要重新進行匯出

  【1】核心點記錄

ServiceBean的兩種暴露服務的方法
1.利用InitializingBean介面,呼叫export()方法(沒有監聽器的情況下才行)
2.利用監聽ContextRefreshedEvent事件達到服務暴露

動態代理生成 Invoker 包裝成 wrapperInvoker

  RegistryProtocol進行注冊
  DubboProtocol對 Invoker 進行匯出,回傳一個Exporter

 ExchangeServer

 

  【2】ServiceBean是怎么行程服務匯出的

//1是利用InitializingBean介面,呼叫export()方法【主要是呼叫父類的export()方法和發布ServiceBeanExportedEvent事件】
//2是利用監聽ContextRefreshedEvent事件達到服務暴露
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware, ApplicationEventPublisherAware {

....

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;

        // 如果某一個Service是通過Spring暴露的,
        // 那么當需要獲取該服務時就要從Spring容器中進行獲取,
        // 也就是從applicationContext中獲取,所以需要把applicationContext添加到SpringExtensionFactory中去
        SpringExtensionFactory.addApplicationContext(applicationContext);
        // 一定要有這一步,不然ServiceBean將接收不到ContextRefreshedEvent事件
        supportedApplicationListener = addApplicationListener(applicationContext, this);
    }

    //當接收到監聽ContextRefreshedEvent事件時候
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 當前服務沒有被匯出并且沒有卸載,才匯出服務
        if (!isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            // 服務匯出(服務注冊)
            export();
        }
    }

    @Override
    @SuppressWarnings({"unchecked", "deprecation"})
    public void afterPropertiesSet() throws Exception {

        // 如果@Service中沒有配置provider
        if (getProvider() == null) {
            // 就從Spring容器中找ProviderConfig型別的Bean
            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
            if (providerConfigMap != null && providerConfigMap.size() > 0) {
                // 從Spring容器中找ProtocolConfig型別的Bean
                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);

                // 如果存在ProtocolConfig存在,并且存在多個ProviderConfig
                if (CollectionUtils.isEmptyMap(protocolConfigMap) && providerConfigMap.size() > 1) { // backward compatibility

                    // 如果找到多個,取第一個default等于true的ProviderConfig
                    List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() != null && config.isDefault()) {
                            providerConfigs.add(config);
                        }
                    }
                    if (!providerConfigs.isEmpty()) {
                        setProviders(providerConfigs);
                    }
                } else {
                    ProviderConfig providerConfig = null;
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault()) {
                            if (providerConfig != null) {
                                throw new IllegalStateException(...);
                            }
                            providerConfig = config;
                        }
                    }
                    if (providerConfig != null) {
                        setProvider(providerConfig);
                    }
                }
            }
        }
        if (getApplication() == null && (getProvider() == null || getProvider().getApplication() == null)) {
            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                ApplicationConfig applicationConfig = null;
                for (ApplicationConfig config : applicationConfigMap.values()) {
                    if (applicationConfig != null) {
                        throw new IllegalStateException(...);
                    }
                    applicationConfig = config;
                }
                if (applicationConfig != null) {
                    setApplication(applicationConfig);
                }
            }
        }
        if (getModule() == null && (getProvider() == null || getProvider().getModule() == null)) {
            Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
            if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                ModuleConfig moduleConfig = null;
                for (ModuleConfig config : moduleConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault()) {
                        if (moduleConfig != null) {
                            throw new IllegalStateException(...);
                        }
                        moduleConfig = config;
                    }
                }
                if (moduleConfig != null) {
                    setModule(moduleConfig);
                }
            }
        }

        // registryIds代碼能看到,但是沒找到在哪里能配置
        if (StringUtils.isEmpty(getRegistryIds())) {
            if (getApplication() != null && StringUtils.isNotEmpty(getApplication().getRegistryIds())) {
                setRegistryIds(getApplication().getRegistryIds());
            }
            if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getRegistryIds())) {
                setRegistryIds(getProvider().getRegistryIds());
            }
        }

        if ((CollectionUtils.isEmpty(getRegistries())) && (getProvider() == null || CollectionUtils.isEmpty(getProvider().getRegistries())) && (getApplication() == null || CollectionUtils.isEmpty(getApplication().getRegistries()))) {
            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
            if (CollectionUtils.isNotEmptyMap(registryConfigMap)) {
                List<RegistryConfig> registryConfigs = new ArrayList<>();
                if (StringUtils.isNotEmpty(registryIds)) {
                    Arrays.stream(COMMA_SPLIT_PATTERN.split(registryIds)).forEach(id -> {
                        if (registryConfigMap.containsKey(id)) {
                            registryConfigs.add(registryConfigMap.get(id));
                        }
                    });
                }

                if (registryConfigs.isEmpty()) {
                    for (RegistryConfig config : registryConfigMap.values()) {
                        if (StringUtils.isEmpty(registryIds) && (config.isDefault() == null || config.isDefault().booleanValue())) {
                            registryConfigs.add(config);
                        }
                    }
                }
                if (!registryConfigs.isEmpty()) {
                    super.setRegistries(registryConfigs);
                }
            }
        }
        if (getMetadataReportConfig() == null) {
            Map<String, MetadataReportConfig> metadataReportConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class, false, false);
            if (metadataReportConfigMap != null && metadataReportConfigMap.size() == 1) {
                super.setMetadataReportConfig(metadataReportConfigMap.values().iterator().next());
            } else if (metadataReportConfigMap != null && metadataReportConfigMap.size() > 1) {
                throw new IllegalStateException("Multiple MetadataReport configs: " + metadataReportConfigMap);
            }
        }

        if (getConfigCenter() == null) {
            Map<String, ConfigCenterConfig> configenterMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConfigCenterConfig.class, false, false);
            if (configenterMap != null && configenterMap.size() == 1) {
                super.setConfigCenter(configenterMap.values().iterator().next());
            } else if (configenterMap != null && configenterMap.size() > 1) {
                throw new IllegalStateException("Multiple ConfigCenter found:" + configenterMap);
            }
        }

        if (getMonitor() == null
                && (getProvider() == null || getProvider().getMonitor() == null)
                && (getApplication() == null || getApplication().getMonitor() == null)) {
            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                MonitorConfig monitorConfig = null;
                for (MonitorConfig config : monitorConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault()) {
                        if (monitorConfig != null) {
                            throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                        }
                        monitorConfig = config;
                    }
                }
                if (monitorConfig != null) {
                    setMonitor(monitorConfig);
                }
            }
        }

        if (getMetrics() == null) {
            Map<String, MetricsConfig> metricsConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class, false, false);
            if (metricsConfigMap != null && metricsConfigMap.size() > 0) {
                MetricsConfig metricsConfig = null;
                for (MetricsConfig config : metricsConfigMap.values()) {
                    if (metricsConfig != null) {
                        throw new IllegalStateException("Duplicate metrics configs: " + metricsConfig + " and " + config);
                    }
                    metricsConfig = config;
                }
                if (metricsConfig != null) {
                    setMetrics(metricsConfig);
                }
            }
        }

        // protocolIds也沒看到在哪里配置
        if (StringUtils.isEmpty(getProtocolIds())) {
            if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getProtocolIds())) {
                setProtocolIds(getProvider().getProtocolIds());
            }
        }

        if (CollectionUtils.isEmpty(getProtocols())
                && (getProvider() == null || CollectionUtils.isEmpty(getProvider().getProtocols()))) {
            Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
            if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
                if (StringUtils.isNotEmpty(getProtocolIds())) {
                    Arrays.stream(COMMA_SPLIT_PATTERN.split(getProtocolIds()))
                            .forEach(id -> {
                                if (protocolConfigMap.containsKey(id)) {
                                    protocolConfigs.add(protocolConfigMap.get(id));
                                }
                            });
                }

                if (protocolConfigs.isEmpty()) {
                    for (ProtocolConfig config : protocolConfigMap.values()) {
                        if (StringUtils.isEmpty(protocolIds)) {
                            protocolConfigs.add(config);
                        }
                    }
                }

                if (!protocolConfigs.isEmpty()) {
                    super.setProtocols(protocolConfigs);
                }
            }
        }
        if (StringUtils.isEmpty(getPath())) {
            if (StringUtils.isNotEmpty(beanName)
                    && StringUtils.isNotEmpty(getInterface())
                    && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }
        //沒有監聽事件才做暴露服務
        if (!supportedApplicationListener) {
            export();
        }
    }


    //服務暴露的核心方法
    @Override
    public void export() {
        super.export();
        // Publish ServiceBeanExportedEvent
        publishExportEvent();
    }

    private void publishExportEvent() {
        ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
        applicationEventPublisher.publishEvent(exportEvent);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

    1)ServiceConfig類#export()方法

public synchronized void export() {
    //讀取配置并補全(最新最全的配置),方法1
    checkAndUpdateSubConfigs();

    // 檢查服務是否需要匯出
    if (!shouldExport()) {
        return;
    }

    // 檢查是否需要延遲發布
    if (shouldDelay()) {
        DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
    } else {
        // 匯出服務,方法2
        doExport();
    }
}

 

    2)方法1:ServiceConfig類#checkAndUpdateSubConfigs()方法

/**
 * 1. ServiceConfig中的某些屬性如果是空的,那么就從ProviderConfig、ModuleConfig、ApplicationConfig中獲取
 * 2. 從配置中心獲取配置,包括應用配置和全域配置
 * 3. 從配置中心獲取Provider配置
 * 4. 從配置中心獲取Protocol配置
 * 5. 如果ApplicationConfig為空,則構造一個ApplicationConfig
 * 6. 從配置中心獲取Registry配置
 * 7. 更新ServiceConfig中的屬性為優先級最高的配置
 * 8. 更新MetadataReportConfig中的屬性為優先級最高的配置
 * 9. 檢查當前服務是不是一個泛化服務
 * 10.檢查Stub和Local
 * 11.檢查Mock
 */
public void checkAndUpdateSubConfigs() {
// ServiceConfig中的某些屬性如果是空的,那么就從ProviderConfig、ModuleConfig、ApplicationConfig中獲取(之前生成的配置Bean)
    completeCompoundConfigs();

    // 方法1.1
    // 從配置中心獲取配置,包括應用配置和全域配置
    // 把獲取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
    // 并重繪所有的Config屬性
    startConfigCenter();

    // 如果沒有ProviderConfig物件,則創建一個
    checkDefault();

    // 如果沒有單獨的配置protocols,那么就從provider獲取配置的協議,添加到的ServiceConfig中去
    // 假如程式員在組態檔中配了一個dubbo協議,配置中心的全域配置或應用配置中也配置了一個協議,那么就會被添加到ServiceConfig中
    checkProtocol();

    checkApplication();

    // if protocol is not injvm checkRegistry
    // 如果protocol不是只有injvm協議,表示服務呼叫不是只在本機jvm里面呼叫,那就需要用到注冊中心
    // 如果protocol是injvm,表示本地呼叫
    if (!isOnlyInJvm()) {
        checkRegistry();
    }

    // 重繪ServiceConfig,方法1.2
    this.refresh();

    // 如果配了metadataReportConfig,那么就重繪配置
    checkMetadataReport();

    if (StringUtils.isEmpty(interfaceName)) {
        throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
    }

    // 當前服務對應的實作類是一個GenericService,表示沒有特定的介面
    if (ref instanceof GenericService) {
        interfaceClass = GenericService.class;
        if (StringUtils.isEmpty(generic)) {
            generic = Boolean.TRUE.toString();
        }
    } else {
        // 加載介面
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        // 重繪MethodConfig,并判斷MethodConfig中對應的方法在介面中是否存在
        checkInterfaceAndMethods(interfaceClass, methods);
        // 實作類是不是該介面型別
        checkRef();
        generic = Boolean.FALSE.toString();
    }
    // local和stub一樣,不建議使用了
    if (local != null) {
        // 如果本地存根為true,則存根類為interfaceName + "Local"
        if (Boolean.TRUE.toString().equals(local)) {
            local = interfaceName + "Local";
        }
        // 加載本地存根類
        Class<?> localClass;
        try {
            localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(localClass)) {
            throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
        }
    }
    // 本地存根
    if (stub != null) {
        // 如果本地存根為true,則存根類為interfaceName + "Stub"
        if (Boolean.TRUE.toString().equals(stub)) {
            stub = interfaceName + "Stub";
        }
        Class<?> stubClass;
        try {
            stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(stubClass)) {
            throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
        }
    }
    // 檢查local和stub
    checkStubAndLocal(interfaceClass);
    // 檢查mock
    checkMock(interfaceClass);
}

 

    3)方法1.1,AbstractInterfaceConfig類#startConfigCenter()方法

void startConfigCenter() {
    if (configCenter == null) {
        ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
    }
    // 如果配置了ConfigCenter
    if (this.configCenter != null) {
        // 從其他位置獲取配置中心的相關屬性資訊,比如配置中心地址
        // TODO there may have duplicate refresh
        this.configCenter.refresh();

        // 屬性更新后,從遠程配置中心獲取資料(應用配置,全域配置)
        prepareEnvironment();
    }
    // 從配置中心取到配置資料后,重繪所有的XxConfig中的屬性
    ConfigManager.getInstance().refreshAll();
}

private void prepareEnvironment() {
    if (configCenter.isValid()) {
        if (!configCenter.checkOrUpdateInited()) {
            return;
        }

        // 動態配置中心,管理臺上的配置中心
        DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());

        // 如果是zookeeper,獲取的就是/dubbo/config/dubbo/dubbo.properties節點中的內容
        String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());

        String appGroup = application != null ? application.getName() : null;
        String appConfigContent = null;
        if (StringUtils.isNotEmpty(appGroup)) {
            // 獲取的就是/dubbo/config/dubbo-demo-consumer-application/dubbo.properties節點中的內容
            // 這里有bug
            appConfigContent = dynamicConfiguration.getProperties (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(), appGroup );
        }
        try {
            Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
            Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
            Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
        } catch (IOException e) {
            throw new IllegalStateException(...);
        }
    }
}

 

    4)方法1.2,AbstractInterfaceConfig類#refresh()方法

// 重繪XxConfig
// 一個XxConfig物件的屬性可能是有值的,也可能是沒有值的,這時需要從其他位置獲取屬性值,來進行屬性的覆寫
// 覆寫的優先級,從大到小為系統變數->配置中心應用配置->配置中心全域配置->注解或xml中定義->dubbo.properties檔案
// 以ServiceConfig為例,ServiceConfig中包括很多屬性,比如timeout
// 但是在定義一個Service時,如果在注解上沒有配置timeout,那么就會其他地方獲取timeout的配置
// 比如可以從系統變數->配置中心應用配置->配置中心全域配置->注解或xml中定義->dubbo.properties檔案
// refresh是重繪,將當前ServiceConfig上的set方法所對應的屬性更新為優先級最高的值
public void refresh() {
    try {
        CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

        // 表示XxConfig物件本身- AbstractConfig
        Configuration config = new ConfigConfigurationAdapter(this);

        //設定順序,
        if (Environment.getInstance().isConfigCenterFirst()) {
            // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
            compositeConfiguration.addConfiguration(4, config);
        } else {
            // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
            compositeConfiguration.addConfiguration(2, config);
        }

        // loop methods, get override value and set the new value back to method
        //
        Method[] methods = getClass().getMethods();
        for (Method method : methods) {
            // 是不是setXX()方法
            if (MethodUtils.isSetter(method)) {
                // 獲取xx配置項的value
                String value =https://www.cnblogs.com/chafry/archive/2022/10/17/ StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                    method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                }
              // 是不是setParameters()方法
            } else if (isParametersSetter(method)) {
                // 獲取parameter配置項的value
                String value =https://www.cnblogs.com/chafry/archive/2022/10/17/ StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                if (StringUtils.isNotEmpty(value)) {
                    Map<String, String> map = invokeGetParameters(getClass(), this);
                    map = map == null ? new HashMap<>() : map;
                    map.putAll(convert(StringUtils.parseParameters(value), ""));
                    invokeSetParameters(getClass(), this, map);
                }
            }
        }
    } catch (Exception e) {
        logger.error("Failed to override ", e);
    }
}

public CompositeConfiguration getConfiguration(String prefix, String id) {
    CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
    // Config center has the highest priority

    // JVM環境變數
    compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
    // 作業系統環境變數
    compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));

    // 配置中心APP配置
    compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));

    // 配置中心Global配置
    compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));

    // dubbo.properties中的配置
    compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
    return compositeConfiguration;
}

 

    5)方法2,ServiceConfig類#doExport()方法

protected synchronized void doExport() {
    // 當前服務已經被取消了,就不能再匯出了
    if (unexported) {
        throw new IllegalStateException(...);
    }
    // 已經匯出了,就不再匯出了
    if (exported) {
        return;
    }
    exported = true;

    if (StringUtils.isEmpty(path)) {
        path = interfaceName;
    }
    doExportUrls();
}

@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
    // 得到url,注冊服務也是一個服務,所以也會有對應的url,通過呼叫該url完成服務注冊
    List<URL> registryURLs = loadRegistries(true);   //

    // 遍歷每個協議
    // 一個協議一個服務
    for (ProtocolConfig protocolConfig : protocols) {
        // path表示服務名
        // contextPath表示應用名(可配置)
        // pathKey = group/contextpath/path:version
        // 例子:myGroup/user/org.apache.dubbo.demo.DemoService:1.0.1

        String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);

        // ProviderModel中存在服務提供者訪問路徑,實作類,介面,以及介面中的各個方法對應的ProviderMethodModel
        // ProviderMethodModel表示某一個方法,方法名,所屬的服務的,
        ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);

        // ApplicationModel表示應用中有哪些服務提供者和參考了哪些服務
        ApplicationModel.initProviderModel(pathKey, providerModel);

        // 每種協議匯出一個單獨的服務,注冊到各個注冊中心
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

 

    6)doExportUrlsFor1Protocol方法

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    // protocolConfig表示某個協議,registryURLs表示所有的注冊中心

    // 如果配置的某個協議,沒有配置name,那么默認為dubbo
    String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }

    // 這個map表示服務url的引數
    Map<String, String> map = new HashMap<String, String>();
    map.put(SIDE_KEY, PROVIDER_SIDE);

    appendRuntimeParameters(map);
    // 監控中心引數
    appendParameters(map, metrics);
    // 應用相關引數
    appendParameters(map, application);
    // 模塊相關引數
    appendParameters(map, module);
    // 提供者相關引數
    appendParameters(map, provider);
    // 協議相關引數
    appendParameters(map, protocolConfig);
    // 服務本身相關引數
    appendParameters(map, this);

    // 服務中某些方法引數
    if (CollectionUtils.isNotEmpty(methods)) {
        for (MethodConfig method : methods) {
            // 某個方法的配置引數,注意有prefix
            appendParameters(map, method, method.getName());
            String retryKey = method.getName() + ".retry";

            // 如果某個方法配置存在xx.retry=false,則改成xx.retry=0
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                if (Boolean.FALSE.toString().equals(retryValue)) {
                    map.put(method.getName() + ".retries", "0");
                }
            }
            List<ArgumentConfig> arguments = method.getArguments();
            if (CollectionUtils.isNotEmpty(arguments)) {
                // 遍歷當前方法配置中的引數配置
                for (ArgumentConfig argument : arguments) {

                    // 如果配置了type,則遍歷當前介面的所有方法,然后找到方法名和當前方法名相等的方法,可能存在多個
                    // 如果配置了index,則看index對應位置的引數型別是否等于type,如果相等,則向map中存入argument物件中的引數
                    // 如果沒有配置index,那么則遍歷方法所有的引數型別,等于type則向map中存入argument物件中的引數
                    // 如果沒有配置type,但配置了index,則把對應位置的argument放入map
                    // convert argument type
                    if (argument.getType() != null && argument.getType().length() > 0) {
                        Method[] methods = interfaceClass.getMethods();
                        // visit all methods
                        if (methods != null && methods.length > 0) {
                            for (int i = 0; i < methods.length; i++) {
                                String methodName = methods[i].getName();
                                // target the method, and get its signature
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method
                                    if (argument.getIndex() != -1) {
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                        } else {
                                            throw new IllegalArgumentException(...);
                                        }
                                    } else {
                                        // multiple callbacks in the method
                                        for (int j = 0; j < argtypes.length; j++) {
                                            Class<?> argclazz = argtypes[j];
                                            if (argclazz.getName().equals(argument.getType())) {
                                                appendParameters(map, argument, method.getName() + "." + j);
                                                if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                    throw new IllegalArgumentException(...);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else if (argument.getIndex() != -1) {
                        appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                    } else {
                        throw new IllegalArgumentException(...);
                    }

                }
            }
        } // end of methods for
    }

    if (ProtocolUtils.isGeneric(generic)) {
        map.put(GENERIC_KEY, generic);
        map.put(METHODS_KEY, ANY_VALUE);
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put(REVISION_KEY, revision);
        }

        // 通過介面對應的Wrapper,拿到介面中所有的方法名字
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }

    // Token是為了防止服務被消費者直接呼叫(偽造http請求)
    // 主要是存于注冊中心,呼叫時Token匹配成功即算通過(所以要求呼叫者是通過注冊中心獲取提供方的資訊)
    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            map.put(TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            map.put(TOKEN_KEY, token);
        }
    }

    // export service
    // 通過該host和port訪問該服務
    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    // 服務url
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
    // url:http://192.168.40.17:80/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=80&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=285072&release=&side=provider&timestamp=1585206500409

    // 可以通過ConfiguratorFactory,在服務匯出時候進行統一配置
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(SCOPE_KEY); // scope可能為null,remote, local,none
    // don't export when none is configured
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
        // 如果scope為none,則不會進行任何的服務匯出,既不會遠程,也不會本地

        // export to local if the config is not remote (export to remote only when config is remote)
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            // 如果scope不是remote,則會進行本地匯出,會把當前url的protocol改為injvm,然后進行匯出
            exportLocal(url);
        }
        // export to remote if the config is not local (export to local only when config is local)
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            // 如果scope不是local,則會進行遠程匯出

            if (CollectionUtils.isNotEmpty(registryURLs)) {
                // 如果有注冊中心,則將服務注冊到注冊中心
                for (URL registryURL : registryURLs) {

                    //if protocol is only injvm ,not register
                    // 如果是injvm,則不需要進行注冊中心注冊
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }

                    // 該服務是否是動態,對應zookeeper上表示是否是臨時節點,對應dubbo中的功能就是靜態服務
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));

                    // 基于注冊中心地址的到監控中心地址,為什么是基于注冊中心地址?
                    URL monitorUrl = loadMonitor(registryURL);

                    // 把監控中心地址添加到服務url中
                    if (monitorUrl != null) {
                        url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                    }

                    // 服務的register引數,如果為true,則表示要注冊到注冊中心
                    if (logger.isInfoEnabled()) {
                        if (url.getParameter(REGISTER_KEY, true)) {
                            logger.info(...);
                        } else {
                            logger.info(...);
                        }
                    }

                    // For providers, this is used to enable custom proxy to generate invoker
                    // 服務使用的動態代理機制,如果為空則使用javassit
                    String proxy = url.getParameter(PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                    }

                    // 生成一個當前服務介面的代理物件
                    // 使用代理生成一個Invoker,Invoker表示服務提供者的代理,可以使用Invoker的invoke方法執行服務
                    // 對應的url為 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472&registry=zookeeper&timestamp=1585207994828
                    // 這個Invoker中包括了服務的實作者、服務介面類、服務的注冊地址(針對當前服務的,引數export指定了當前服務)
                    // 此invoker表示一個可執行的服務,呼叫invoker的invoke()方法即可執行服務,同時此invoker也可用來匯出
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                    // DelegateProviderMetaDataInvoker也表示服務提供者,包括了Invoker和服務的配置
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    // 使用特定的協議來對服務進行匯出,這里的協議為RegistryProtocol,匯出成功后得到一個Exporter
                    // 1. 先使用RegistryProtocol進行服務注冊
                    // 2. 注冊完了之后,使用DubboProtocol進行匯出
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            } else {
                // 沒有配置注冊中心時,也會匯出服務

                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = protocol.export(wrapperInvoker);
                exporters.add(exporter);
            }


            // 根據服務url,講服務的元資訊存入元資料中心
            MetadataReportService metadataReportService = null;
            if ((metadataReportService = getMetadataReportService()) != null) {
                metadataReportService.publishProvider(url);
            }
        }
    }
    this.urls.add(url);
}

 

    7)protocol.export的呼叫

@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // 匯出服務
    // registry://   ---> RegistryProtocol
    // zookeeper://  ---> ZookeeperRegistry
    // dubbo://      ---> DubboProtocol
    // provider://   --->

    // 將registry://xxx?xx=xx&registry=zookeeper 轉為---> zookeeper://xxx?xx=xx
    URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0&timestamp=1590735956479
    // 得到服務提供者url
    URL providerUrl = getProviderUrl(originInvoker); // dubbo://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=27656&release=2.7.0&side=provider&timeout=3000&timestamp=1590735956489

    // Subscribe the override data
    // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
    //  the same service. Because the subscribed is cached key with the name of the service, it causes the
    //  subscription information to cover.

    // overrideSubscribeUrl是老版本的動態配置監聽url,表示了需要監聽的服務以及監聽的型別(configurators, 這是老版本上的動態配置)
    // 在服務提供者url的基礎上,生成一個overrideSubscribeUrl,協議為provider://,增加引數category=configurators&check=false
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);

    // 一個overrideSubscribeUrl對應一個OverrideListener,用來監聽變化事件,監聽到overrideSubscribeUrl的變化后,
    // OverrideListener就會根據變化進行相應處理,具體處理邏輯看OverrideListener的實現
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);


    // 在這個方法里會利用providerConfigurationListener和serviceConfigurationListener去重寫providerUrl
    // providerConfigurationListener表示應用級別的動態配置監聽器,providerConfigurationListener是RegistyProtocol的一個屬性
    // serviceConfigurationListener表示服務級別的動態配置監聽器,serviceConfigurationListener是在每暴露一個服務時就會生成一個
    // 這兩個監聽器都是新版本中的監聽器
    // 新版本監聽的zk路徑是:
    // 服務: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators節點的內容
    // 應用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators節點的內容
    // 注意,要喝配置中心的路徑區分開來,配置中心的路徑是:
    // 應用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties節點的內容
    // 全域:/dubbo/config/dubbo/dubbo.properties節點的內容
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);

    // export invoker
    // 根據動態配置重寫了providerUrl之后,就會呼叫DubboProtocol或HttpProtocol去進行匯出服務了
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
    // 得到注冊中心-ZookeeperRegistry
    final Registry registry = getRegistry(originInvoker);

    // 得到存入到注冊中心去的providerUrl,會對服務提供者url中的引數進行簡化
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);

    // 將當前服務提供者Invoker,以及該服務對應的注冊中心地址,以及簡化后的服務url存入ProviderConsumerRegTable
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);


    //to judge if we need to delay publish
    //是否需要注冊到注冊中心
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        // 注冊服務,把簡化后的服務提供者url注冊到registryUrl中去
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }

    // 針對老版本的動態配置,需要把overrideSubscribeListener系結到overrideSubscribeUrl上去進行監聽
    // 兼容老版本的配置修改,利用overrideSubscribeListener去監聽舊版本的動態配置變化
    // 監聽overrideSubscribeUrl   provider://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=416332&release=&side=provider&timestamp=1585318241955
    // 那么新版本的providerConfigurationListener和serviceConfigurationListener是在什么時候進行訂閱的呢?在這兩個類構造的時候
    // Deprecated! Subscribe to override rules in 2.6.x or before.
    // 老版本監聽的zk路徑是:/dubbo/org.apache.dubbo.demo.DemoService/configurators/override://0.0.0.0/org.apache.dubbo.demo.DemoService?category=configurators&compatible_config=true&dynamic=false&enabled=true&timeout=6000
    // 監聽的是路徑的內容,不是節點的內容
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);


    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}

public void register(URL registryUrl, URL registeredProviderUrl) {
    Registry registry = registryFactory.getRegistry(registryUrl);
    // 呼叫FailbackRegistry類的方法再轉到ZookeeperRegistry的register方法
    registry.register(registeredProviderUrl);
}

//FailbackRegistry類(進行失敗重試)
@Override
public void register(URL url) {
    super.register(url);
    removeFailedRegistered(url);
    removeFailedUnregistered(url);
    try {
        // 這里才會呼叫ZookeeperRegistry類的方法
        doRegister(url);
    } catch (Exception e) {
        Throwable t = e;

        // If the startup detection is opened, the Exception is thrown directly.
        boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true) && !CONSUMER_PROTOCOL.equals(url.getProtocol());
        boolean skipFailback = t instanceof SkipFailbackWrapperException;
        if (check || skipFailback) {
            if (skipFailback) {
                t = t.getCause();
            }
            throw new IllegalStateException(...);
        } else {
            logger.error(...);
        }
        addFailedRegistered(url);
    }
}

//ZookeeperRegistry的真正注冊的地方
@Override
public void doRegister(URL url) {
    try {
        zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
    } catch (Throwable e) {
        throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

    8)doLocalExport方法

@SuppressWarnings("unchecked")
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
    String key = getCacheKey(originInvoker);

    return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
        Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
        // protocol屬性的值是哪來的,是在SPI中注入進來的,是一個代理類
        // 這里實際利用的就是DubboProtocol或HttpProtocol去export  NettyServer
        // 為什么需要ExporterChangeableWrapper?方便注銷已經被匯出的服務
        return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
    });
}

@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    URL url = invoker.getUrl();

    // export service.
    String key = serviceKey(url);
    // 構造一個Exporter
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    exporterMap.put(key, exporter);

    //export an stub service for dispatching event
    Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
    Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
    if (isStubSupportEvent && !isCallbackservice) {
        String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
        if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
            if (logger.isWarnEnabled()) {
                logger.warn(...);
            }
        } else {
            // 服務的stub方法
            stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
        }
    }

    // 開啟NettyServer
    openServer(url);

    optimizeSerialization(url);

    return exporter;
}

private void openServer(URL url) {
    // find server.
    String key = url.getAddress(); // 獲得ip地址和port, 192.168.40.17:20880

    // NettyClient, NettyServer
    //client can export a service which's only for server to invoke
    boolean isServer = url.getParameter(IS_SERVER_KEY, true);
    if (isServer) {
        // 快取Server物件
        ExchangeServer server = serverMap.get(key);

        // DCL,Double Check Lock
        if (server == null) {
            synchronized (this) {
                server = serverMap.get(key);
                if (server == null) {
                    // 創建Server,并進行快取
                    serverMap.put(key, createServer(url));
                }
            }
        } else {
            // server supports reset, use together with override
            // 服務重新匯出時,就會走這里
            server.reset(url);
        }
    }
}

private ExchangeServer createServer(URL url) {
    url = URLBuilder.from(url)
            // send readonly event when server closes, it's enabled by default
            .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
            // enable heartbeat by default
            .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
            .addParameter(CODEC_KEY, DubboCodec.NAME)
            .build();

    // 協議的服務器端實作型別,比如:dubbo協議的mina,netty等,http協議的jetty,servlet等,默認為netty
    String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);

    if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
        throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    }

    // 通過url系結埠,和對應的請求處理器
    ExchangeServer server;
    try {
        // requestHandler是請求處理器,型別為ExchangeHandler
        // 表示從url的埠接收到請求后,requestHandler來進行處理
        server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
        throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    }

    // 協議的客戶端實作型別,比如:dubbo協議的mina,netty等
    str = url.getParameter(CLIENT_KEY);
    if (str != null && str.length() > 0) {
        Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
        if (!supportedTypes.contains(str)) {
            throw new RpcException("Unsupported client type: " + str);
        }
    }

    return server;
}

    9)當資料發生改變時 OverrideListener 監聽者的處理

@Override
public synchronized void notify(List<URL> urls) {

    List<URL> matchedUrls = getMatchedUrls(urls, subscribeUrl.addParameter(CATEGORY_KEY, CONFIGURATORS_CATEGORY));
    // No matching results
    if (matchedUrls.isEmpty()) {
        return;
    }

    // 對發生了變化的url進行過濾,只取url是override協議,或者引數category等于configurators的url
    this.configurators = Configurator.toConfigurators(classifyUrls(matchedUrls, UrlUtils::isConfigurator)).orElse(configurators);
    // 根據Override協議修改
    doOverrideIfNecessary();
}

public synchronized void doOverrideIfNecessary() {
    final Invoker<?> invoker;
    if (originInvoker instanceof InvokerDelegate) {
        invoker = ((InvokerDelegate<?>) originInvoker).getInvoker();
    } else {
        invoker = originInvoker;
    }
    //The origin invoker 當前服務的原始服務提供者url
    URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
    String key = getCacheKey(originInvoker);

    ExporterChangeableWrapper<?> exporter = bounds.get(key);
    if (exporter == null) {
        logger.warn(new IllegalStateException("error state, exporter should not be null"));
        return;
    }

    //The current, may have been merged many times,當前服務被匯出的url
    URL currentUrl = exporter.getInvoker().getUrl();

    //根據configurators修改url,configurators是全量的,并不是某個新增的或洗掉的,所以是基于原始的url進行修改,并不是基于currentUrl
    //Merged with this configuration
    URL newUrl = getConfigedInvokerUrl(configurators, originUrl);

    newUrl = getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl);
    newUrl = getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey()).getConfigurators(), newUrl);

    // 修改過的url如果和目前的url不相同,則重新按newUrl匯出
    if (!currentUrl.equals(newUrl)) {
        RegistryProtocol.this.reExport(originInvoker, newUrl);
        logger.info(...);
    }
}

public <T> void reExport(final Invoker<T> originInvoker, URL newInvokerUrl) {

    // 根據newInvokerUrl進行匯出
    // update local exporter
    ExporterChangeableWrapper exporter = doChangeLocalExport(originInvoker, newInvokerUrl);

    // 獲取準確的ProviderUrl
    // update registry
    URL registryUrl = getRegistryUrl(originInvoker);
    // 對于一個服務提供者url,在注冊到注冊中心時,會先進行簡化,所以如果
    final URL registeredProviderUrl = getRegisteredProviderUrl(newInvokerUrl, registryUrl);

    //decide if we need to re-publish
    // 根據getServiceKey獲取ProviderInvokerWrapper
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.getProviderWrapper(registeredProviderUrl, originInvoker);
    // 生成一個新的ProviderInvokerWrapper
    ProviderInvokerWrapper<T> newProviderInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);

    /**
     * Only if the new url going to Registry is different with the previous one should we do unregister and register.
     * 如果新的服務提供者url簡化后的url和這個服務之前的服務提供者url簡化后的url不相等,則需要把新的簡化后的服務提供者url注冊到注冊中心去
     */
    if (providerInvokerWrapper.isReg() && !registeredProviderUrl.equals(providerInvokerWrapper.getProviderUrl())) {
        unregister(registryUrl, providerInvokerWrapper.getProviderUrl());
        register(registryUrl, registeredProviderUrl);
        newProviderInvokerWrapper.setReg(true);
    }

    exporter.setRegisterUrl(registeredProviderUrl);
}

@SuppressWarnings("unchecked")
private <T> ExporterChangeableWrapper doChangeLocalExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
    String key = getCacheKey(originInvoker);
    final ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
    if (exporter == null) {
        logger.warn(new IllegalStateException("error state, exporter should not be null"));
    } else {
        // 到這里才能真正明白,為什么需要InvokerDelegate
        // InvokerDelegate表示一個呼叫者,由invoker+url構成,invoker不變,url可變
        final Invoker<T> invokerDelegate = new InvokerDelegate<T>(originInvoker, newInvokerUrl);
        //這次openServer會走HeaderExchangeServer的reset方法
        exporter.setExporter(protocol.export(invokerDelegate));
    }
    return exporter;
}

//這里面存在重新匯出,關閉舊的延遲任務(舊的心跳任務),開啟新的心跳任務,但是Netty不會關閉,也不會重啟
@Override
public void reset(URL url) {
    server.reset(url);
    try {
        int currHeartbeat = getHeartbeat(getUrl());
        int currIdleTimeout = getIdleTimeout(getUrl());
        int heartbeat = getHeartbeat(url);
        int idleTimeout = getIdleTimeout(url);
        if (currHeartbeat != heartbeat || currIdleTimeout != idleTimeout) {
            cancelCloseTask();
            startIdleCheckTask(url);
        }
    } catch (Throwable t) {
        logger.error(t.getMessage(), t);
    }
}

 

  【3】匯總

服務匯出的入口為ServiceBean中的export()方法,當Spring啟動完之后,通過接收Spring的ContextRefreshedEvent事件來觸發export()方法的執行,

一個ServiceBean物件就表示一個Dubbo服務,ServiceBean物件中的引數就表示服務的引數,比如timeout,該物件的引數值來至@Service注解中所定義的,

服務匯出主要得做兩件事情:
1. 根據服務的引數資訊,啟動對應的網路服務器(netty、tomcat、jetty等),用來接收網路請求
2. 將服務的資訊注冊到注冊中心

但是在做這兩件事情之前得先把服務的引數確定好,因為一個Dubbo服務的引數,除開可以在@Service注解中去配置,還會繼承Dubbo服務所屬應用(Application)上的配置,
還可以在配置中心或JVM環境變數中去配置某個服務的引數,所以首先要做的是確定好當前服務最終的(優先級最高)的引數值, 確定好服務引數之后,就根據所配置的協議啟動對應的網路服務器,在啟動網路服務器時,并且在網路服務器接收請求的程序中,都可以從服務引數中獲取資訊,比如最大連接數,執行緒數,socket超時時間等等, 啟動完網路服務器之后,就將服務資訊注冊到注冊中心,同時還有向注冊中心注冊監聽器,監聽Dubbo的中的動態配置資訊變更,

 

Dubbo服務引入

  【0】核心點記錄

生成代理物件(代理物件應該包含的功能){
    1.獲取服務提供者串列
    2.Mock--------MockClusterInvoker
    3.路由篩選
    4.負載均衡
    5.集群容錯------FailoverClusterInvoker
    6.構造NettyClient
    7.發送資料(Invocation)
}

代理物件的Invoker
MockClusterInvoker
    Invoker屬性塞入FailoverClusterInvoker
        FailoverClusterInvoker
            Invoker屬性塞入DubboInvoker

 

  【1】服務要怎么引入(ReferenceConfig類#get()方法)

//服務引入的入口方法
public synchronized T get() {
    //讀取配置并補全(最新最全的配置)
    checkAndUpdateSubConfigs();
    if (destroyed) {
        throw new IllegalStateException(...);
    }
    if (ref == null) {
        // 入口
        init();
    }
    return ref;  // Invoke代理
}

 

  【2】檢查并拿到最新配置(ReferenceConfig類#checkAndUpdateSubConfigs()方法)

public void checkAndUpdateSubConfigs() {
  if (StringUtils.isEmpty(interfaceName)) {
      throw new IllegalStateException(...);
  }
  // 填充ReferenceConfig物件中的屬性
  completeCompoundConfigs();
  // 開啟配置中心
  startConfigCenter();
  // get consumer's global configuration
  checkDefault();
  // 重繪ReferenceConfig物件的屬性值
  this.refresh();

  // 設定泛化
  if (getGeneric() == null && getConsumer() != null) {
      setGeneric(getConsumer().getGeneric());
  }

  if (ProtocolUtils.isGeneric(getGeneric())) {
      interfaceClass = GenericService.class;
  } else {
      try {
          interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
      } catch (ClassNotFoundException e) {
          throw new IllegalStateException(...);
      }
      checkInterfaceAndMethods(interfaceClass, methods);
  }


  resolveFile();
  checkApplication();
  checkMetadataReport();
}

 

  【3】初始化生成代理物件(ReferenceConfig類#init()方法)

private void init() {
    if (initialized) {
        return;
    }

    //準備引數,進行引數配置
    checkStubAndLocal(interfaceClass);
    checkMock(interfaceClass);
    Map<String, String> map = new HashMap<String, String>();

    map.put(SIDE_KEY, CONSUMER_SIDE);

    appendRuntimeParameters(map);
    if (!ProtocolUtils.isGeneric(getGeneric())) {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put(REVISION_KEY, revision);
        }

        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
        }
    }
    map.put(INTERFACE_KEY, interfaceName);
    appendParameters(map, metrics);
    appendParameters(map, application);
    appendParameters(map, module);
    // remove 'default.' prefix for configs from ConsumerConfig
    // appendParameters(map, consumer, Constants.DEFAULT_KEY);
    appendParameters(map, consumer);
    appendParameters(map, this);

    Map<String, Object> attributes = null;
    if (CollectionUtils.isNotEmpty(methods)) {
        attributes = new HashMap<String, Object>();
        for (MethodConfig methodConfig : methods) {
            appendParameters(map, methodConfig, methodConfig.getName());
            String retryKey = methodConfig.getName() + ".retry";
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                if ("false".equals(retryValue)) {
                    map.put(methodConfig.getName() + ".retries", "0");
                }
            }

            attributes.put(methodConfig.getName(), convertMethodConfig2AsyncInfo(methodConfig));
        }
    }

    String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
    if (StringUtils.isEmpty(hostToRegistry)) {
        hostToRegistry = NetUtils.getLocalHost();
    } else if (isInvalidLocalHost(hostToRegistry)) {
        throw new IllegalArgumentException(...);
    }
    map.put(REGISTER_IP_KEY, hostToRegistry);
    
    //引數配置完成后去生成代理物件
    ref = createProxy(map);

    String serviceKey = URL.buildKey(interfaceName, group, version);
    ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes));
    initialized = true;
}

 

  【4】生成代理物件(ReferenceConfig類#createProxy()方法)

@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
private T createProxy(Map<String, String> map) {
    if (shouldJvmRefer(map)) {
        // injvm://
        URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
        invoker = REF_PROTOCOL.refer(interfaceClass, url);
    } else {
        // 為什么會有urls,因為可以在@Reference的url屬性中配置多個url,可以是點對點的服務地址,也可以是注冊中心的地址
        urls.clear(); // reference retry init will add url to urls, lead to OOM
        // @Reference中指定了url屬性
        if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
            String[] us = SEMICOLON_SPLIT_PATTERN.split(url); // 用;號切分
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        url = url.setPath(interfaceName);
                    }

                    // 如果是注冊中心地址,則在url中添加一個refer引數
                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        // map表示消費者端配置的引數
                        urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                    } else {
                        // 如果是服務地址
                        // 有可能url中配置了引數,map中表示的服務消費者消費服務時的引數,所以需要合并
                        urls.add(ClusterUtils.mergeUrl(url, map));
                    }
                }
            }
        } else { // assemble URL from register center's configuration
            // @Reference中的protocol屬性表示使用哪個協議呼叫服務,如果不是本地呼叫協議injvm://,則把注冊中心地址找出來
            // 對于injvm://協議已經在之前的邏輯中就已經生成invoke了
            // if protocols not injvm checkRegistry
            if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){
                checkRegistry();
                // 加載注冊中心地址
                List<URL> us = loadRegistries(false);
                if (CollectionUtils.isNotEmpty(us)) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        // 對于注冊中心地址都添加REFER_KEY
                        urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException(...);
                }
            }
        }

        // 如果只有一個url則直接refer得到一個invoker
        if (urls.size() == 1) {
            // RegistryProtocol.refer() 或者 DubboProtocol.refer()
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } else {
            // 如果有多個url
            // 1. 根據每個url,refer得到對應的invoker
            // 2. 如果這多個urls中存在注冊中心url,則把所有invoker整合為RegistryAwareClusterInvoker,該Invoker在呼叫時,會查看所有Invoker中是否有默認的,如果有則使用默認的Invoker,如果沒有,則使用第一個Invoker
            // 2. 如果這多個urls中不存在注冊中心url,則把所有invoker整合為FailoverCluster

            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null; // 用來記錄urls中最后一個注冊中心url
            for (URL url : urls) {
                invokers.add(REF_PROTOCOL.refer(interfaceClass, url));

                if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                    registryURL = url; // use last registry url
                }
            }

            // 如果存在注冊中心地址
            if (registryURL != null) { // registry url is available
                // use RegistryAwareCluster only when register's CLUSTER is available
                URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                // StaticDirectory表示靜態服務目錄,里面的invokers是不會變的, 生成一個RegistryAwareCluster
                // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                invoker = CLUSTER.join(new StaticDirectory(u, invokers));
            } else { // not a registry url, must be direct invoke.
                // 如果不存在注冊中心地址, 生成一個FailoverClusterInvoker
                invoker = CLUSTER.join(new StaticDirectory(invokers));
            }
        }
    }

    if (shouldCheck() && !invoker.isAvailable()) {
        throw new IllegalStateException(...);
    }
 
    MetadataReportService metadataReportService = null;
    if ((metadataReportService = getMetadataReportService()) != null) {
        URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
        metadataReportService.publishConsumer(consumerURL);
    }
    // create service proxy
    return (T) PROXY_FACTORY.getProxy(invoker);
}

 

  【5】PROXY_FACTORY.getProxy等程序

//默認采用JavassistProxyFactory來產生代理物件
public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {

        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        // 如果現在被代理的物件proxy本身就是一個已經被代理過的物件,那么則取代理類的Wrapper,否則取type(介面)的Wrapper
        // Wrapper是針對某個類或某個介面的包裝類,通過wrapper物件可以更方便的去執行某個類或某個介面的方法
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

        // proxy是服務實作類
        // type是服務介面
        // url是一個注冊中心url,但同時也記錄了
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable {

                // 執行proxy的method方法
                // 執行的proxy實體的方法
                // 如果沒有wrapper,則要通過原生的反射技術去獲取Method物件,然后執行
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

public class InvokerInvocationHandler implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }

        // 這里的recreate方法很重要,他會呼叫AppResponse的recreate方法,
        // 如果AppResponse物件中存在exception資訊,則此方法中會throw這個例外
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

 

 

  【6】對URL的處理程序

    1)從@Reference的url屬性中配置多個url,然后采用字串分割的形式拿出來,包裝后塞入urls串列中

    2)加載注冊中心地址

protected List<URL> loadRegistries(boolean provider) {
    // check && override if necessary
    List<URL> registryList = new ArrayList<URL>();
    if (CollectionUtils.isNotEmpty(registries)) {
        for (RegistryConfig config : registries) {
            String address = config.getAddress();
            // 如果注冊中心沒有配地址,則地址為0.0.0.0
            if (StringUtils.isEmpty(address)) {
                address = ANYHOST_VALUE;
            }
            // 如果注冊中心的地址不是"N/A"
            if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                Map<String, String> map = new HashMap<String, String>();
                // 把application中的引數放入map中,注意,map中的key是沒有prefix的
                appendParameters(map, application);
                // 把config中的引數放入map中,注意,map中的key是沒有prefix的
                // config是RegistryConfig,表示注冊中心
                appendParameters(map, config);
                // 此處path值固定為RegistryService.class.getName(),因為現在是在加載注冊中心
                map.put(PATH_KEY, RegistryService.class.getName());
                // 把dubbo的版本資訊和pid放入map中
                appendRuntimeParameters(map);

                // 如果map中如果沒有protocol,那么默認為dubbo
                if (!map.containsKey(PROTOCOL_KEY)) {
                    map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                }

                // 構造注冊中心url,地址+引數
                List<URL> urls = UrlUtils.parseURLs(address, map);

                for (URL url : urls) {
                    url = URLBuilder.from(url)
                            .addParameter(REGISTRY_KEY, url.getProtocol())
                            .setProtocol(REGISTRY_PROTOCOL)
                            .build();
                    // 到此為止,url的內容大概為:
                    // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936&registry=zookeeper&timestamp=1584886077813
                    // 該url表示:使用registry協議呼叫org.apache.dubbo.registry.RegistryService服務
                    // 引數為application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936&registry=zookeeper&timestamp=1584886077813

                    // 這里是服務提供者和服務消費者區別的邏輯
                    // 如果是服務提供者,獲取register的值,如果為false,表示該服務不注冊到注冊中心
                    // 如果是服務消費者,獲取subscribe的值,如果為false,表示該引入的服務不訂閱注冊中心中的資料
                    if ((provider && url.getParameter(REGISTER_KEY, true))
                            || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                        registryList.add(url);
                    }
                }
            }
        }
    }
    return registryList;
}

 

  【7】invoker的包裝程序

    1)前置說明

//RegistryProtocol實際上會被兩個包裝類包裝ProtocolListenerWrapper與ProtocolFilterWrapper
//如ProtocolListenerWrapper的protocol屬性存放ProtocolFilterWrapper
//ProtocolFilterWrapper的protocol屬性存放RegistryProtocol

//然后是Cluster,通過介面可知默認是FailoverCluster,但實際上還會有一個包裝類MockClusterWrapper
//MockClusterWrapper的cluster屬性存放FailoverCluster或者RegistryAwareCluster

 

    2)先是呼叫REF_PROTOCOL.refer

//ProtocolListenerWrapper的處理
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        return protocol.refer(type, url);
    }
    return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
            Collections.unmodifiableList(
                    ExtensionLoader.getExtensionLoader(InvokerListener.class)
                            .getActivateExtension(url, INVOKER_LISTENER_KEY)));
}
//ProtocolFilterWrapper的處理
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        return protocol.refer(type, url);
    }
    return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    // 根據url獲取filter,根據url中的parameters取key為key的value所對應的filter,但是還會匹配group
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

    // ConsumerContextFilter--->FutureFilter--->MonitorFilter
    // ConsumerContextFilter用來設定RpcContext
    if (!filters.isEmpty()) {
        for (int i = filters.size() - 1; i >= 0; i--) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {

                @Override
                public Class<T> getInterface() {
                    return invoker.getInterface();
                }

                @Override
                public URL getUrl() {
                    return invoker.getUrl();
                }

                @Override
                public boolean isAvailable() {
                    return invoker.isAvailable();
                }

                @Override
                public Result invoke(Invocation invocation) throws RpcException {
                    Result asyncResult;
                    try {
                        // 得到一個異步結果
                        asyncResult = filter.invoke(next, invocation);
                    } catch (Exception e) {
                        // one rror callback
                        if (filter instanceof ListenableFilter) {
                            Filter.Listener listener = ((ListenableFilter) filter).listener();
                            if (listener != null) {
                                listener.onError(e, invoker, invocation);
                            }
                        }
                        throw e;
                    }
                    return asyncResult;
                }

                @Override
                public void destroy() {
                    invoker.destroy();
                }

                @Override
                public String toString() {
                    return invoker.toString();
                }
            };
        }
    }

    return new CallbackRegistrationInvoker<>(last, filters);
}

//RegistryProtocol的處理
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

    // 從registry://的url中獲取對應的注冊中心,比如zookeeper, 默認為dubbo,dubbo提供了自帶的注冊中心實作
    // url由 registry:// 改變為---> zookeeper://
    url = URLBuilder.from(url)
            .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
            .removeParameter(REGISTRY_KEY)
            .build();

    // 拿到注冊中心實作,ZookeeperRegistry
    Registry registry = registryFactory.getRegistry(url);

    // 下面這個代碼,通過過git歷史提交記錄是用來解決SimpleRegistry不可用的問題
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // qs表示 queryString, 表示url中的引數,表示消費者引入服務時所配置的引數
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));

    // group="a,b" or group="*"
    String group = qs.get(GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            // group有多個值,這里的cluster為MergeableCluster
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }

    // 這里的cluster是cluster的Adaptive物件
    return doRefer(cluster, registry, type, url);
}

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    // RegistryDirectory表示動態服務目錄,會和注冊中心的資料保持同步
    // type表示一個服務對應一個RegistryDirectory,url表示注冊中心地址
    // 在消費端,最核心的就是RegistryDirectory
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    directory.setRegistry(registry);
    directory.setProtocol(protocol);


    // all attributes of REFER_KEY
    // 引入服務所配置的引數
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());

    // 消費者url
    URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
    if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
        directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));

        // 注冊簡化后的消費url
        registry.register(directory.getRegisteredConsumerUrl());
    }

    // 構造路由鏈,路由鏈會在引入服務時按路由條件進行過濾
    // 路由鏈是動態服務目錄中的一個屬性,通過路由鏈可以過濾某些服務提供者
    directory.buildRouterChain(subscribeUrl);

    // 服務目錄需要訂閱的幾個路徑
    // 當前所引入的服務的消費應用目錄:/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
    // 當前所引入的服務的動態配置目錄:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators
    // 當前所引入的服務的提供者目錄:/dubbo/org.apache.dubbo.demo.DemoService/providers
    // 當前所引入的服務的老版本動態配置目錄:/dubbo/org.apache.dubbo.demo.DemoService/configurators
    // 當前所引入的服務的老版本路由器目錄:/dubbo/org.apache.dubbo.demo.DemoService/routers
    directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
            PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

    // 利用傳進來的cluster,join得到invoker,
    Invoker invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}

 

    3)再是呼叫CLUSTER.join

//MockClusterWrapper的處理
public class MockClusterWrapper implements Cluster {
    private Cluster cluster;
    public MockClusterWrapper(Cluster cluster) {
        this.cluster = cluster;
    }

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory));
    }
}

//有注冊中心,RegistryAwareCluster的處理
public class RegistryAwareCluster implements Cluster {
    public final static String NAME = "registryaware";

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new RegistryAwareClusterInvoker<T>(directory);
    }
}

//沒有注冊中心,FailoverCluster的處理
public class FailoverCluster implements Cluster {
    public final static String NAME = "failover";
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<T>(directory);
    }
}

 

  【8】RegistryProtocol里面的監聽程序

 //directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

//RegistryDirectory類#subscribe方法
public void subscribe(URL url) {
    setConsumerUrl(url);
    CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this); // 監聽consumer應用
    serviceConfigurationListener = new ReferenceConfigurationListener(this, url); // 監聽所引入的服務的動態配置
    registry.subscribe(url, this); //老版本的監聽
}

//FailbackRegistry類#subscribe方法
@Override
public void subscribe(URL url, NotifyListener listener) {
    super.subscribe(url, listener);
    removeFailedSubscribed(url, listener);
    try {
        // 核心方法
        doSubscribe(url, listener);
    } catch (Exception e) {
        Throwable t = e;

        List<URL> urls = getCacheUrls(url);
        if (CollectionUtils.isNotEmpty(urls)) {
            notify(url, listener, urls);
            logger.error(...);
        } else {
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }
        }

        // Record a failed registration request to a failed list, retry regularly
        // 添加listener,向zk添加監聽器時如果報錯了,那么會把這個listener添加到failedSubscribed中,并會定時重試(重新注冊listener)
        addFailedSubscribed(url, listener);
    }
}

// 父類AbstractRegistry#subscribe方法
// 把listener添加到subscribed中,subscribed是一個map, 存的是URL:Set<NotifyListener>
@Override
public void subscribe(URL url, NotifyListener listener) {
    if (url == null) {
        throw new IllegalArgumentException("subscribe url == null");
    }
    if (listener == null) {
        throw new IllegalArgumentException("subscribe listener == null");
    }
    if (logger.isInfoEnabled()) {
        logger.info("Subscribe: " + url);
    }
    Set<NotifyListener> listeners = subscribed.computeIfAbsent(url, n -> new ConcurrentHashSet<>());
    listeners.add(listener);
}

// 進行訂閱,先看父類的subscribe方法
@Override
public void doSubscribe(final URL url, final NotifyListener listener) {
    try {
        if (ANY_VALUE.equals(url.getServiceInterface())) {
            // 訂閱所有服務

            String root = toRootPath();
            ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
            if (listeners == null) {
                zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                listeners = zkListeners.get(url);
            }
            ChildListener zkListener = listeners.get(listener);
            if (zkListener == null) {
                listeners.putIfAbsent(listener, (parentPath, currentChilds) -> {
                    for (String child : currentChilds) {
                        child = URL.decode(child);
                        if (!anyServices.contains(child)) {
                            anyServices.add(child);
                            subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
                                    Constants.CHECK_KEY, String.valueOf(false)), listener);
                        }
                    }
                });
                zkListener = listeners.get(listener);
            }
            zkClient.create(root, false);
            List<String> services = zkClient.addChildListener(root, zkListener);
            if (CollectionUtils.isNotEmpty(services)) {
                for (String service : services) {
                    service = URL.decode(service);
                    anyServices.add(service);
                    subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
                            Constants.CHECK_KEY, String.valueOf(false)), listener);
                }
            }
        } else {
            // 單獨訂閱某一個服務

            List<URL> urls = new ArrayList<>();
            // 得到真正要監聽的zk上的路徑,
            for (String path : toCategoriesPath(url)) {
                // 根據監聽地址去拿listeners,如果沒有則生成
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                if (listeners == null) {
                    zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                    listeners = zkListeners.get(url);
                }

                // 一個NotifyListener對應一個ChildListener
                ChildListener zkListener = listeners.get(listener);
                if (zkListener == null) {
                    // lambda運算式就是監聽邏輯, parentPath表示父path,currentChilds表示當前擁有的child, 會呼叫notify方法進行實際的處理
                    listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                    zkListener = listeners.get(listener);
                }
                // 創建zk上路徑
                zkClient.create(path, false);

                // 添加真正跟zk相關的ChildListener,ChildListener中的邏輯就是監聽到zk上資料發生了變化后會觸發的邏輯
                List<String> children = zkClient.addChildListener(path, zkListener);
                if (children != null) {
                    urls.addAll(toUrlsWithEmpty(url, path, children));
                }
            }
            // 這里的urls就是從現在所引入的服務的目錄下查到的url,比如下面這個三個目錄下的路徑
//                "/dubbo/org.apache.dubbo.demo.DemoService/providers"
//                "/dubbo/org.apache.dubbo.demo.DemoService/configurators"
//                "/dubbo/org.apache.dubbo.demo.DemoService/routers"
            notify(url, listener, urls);
        }
    } catch (Throwable e) {
        throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

 

  【9】監聽器觸發時

//RegistryDirectory類#notify方法
public synchronized void notify(List<URL> urls) {
    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)
            .filter(this::isValidCategory)
            .filter(this::isNotCompatibleFor26x)
            .collect(Collectors.groupingBy(url -> {
                if (UrlUtils.isConfigurator(url)) {
                    return CONFIGURATORS_CATEGORY;
                } else if (UrlUtils.isRoute(url)) {
                    return ROUTERS_CATEGORY;
                } else if (UrlUtils.isProvider(url)) {
                    return PROVIDERS_CATEGORY;
                }
                return "";
            }));

    // 獲取動態配置URL,生成configurators
    List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

    // 獲取老版本路由URL,生成Router,并添加到路由鏈中
    List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
    toRouters(routerURLs).ifPresent(this::addRouters);

    // 獲取服務提供者URL
    List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
    refreshOverrideAndInvoker(providerURLs);
}

private void refreshOverrideAndInvoker(List<URL> urls) {
    // mock zookeeper://xxx?mock=return null
    overrideDirectoryUrl();
    refreshInvoker(urls);
}

// 利用動態配置重寫服務目錄地址
private void overrideDirectoryUrl() {
    // merge override parameters
    this.overrideDirectoryUrl = directoryUrl;
    List<Configurator> localConfigurators = this.configurators; // local reference
    doOverrideUrl(localConfigurators);

    List<Configurator> localAppDynamicConfigurators = CONSUMER_CONFIGURATION_LISTENER.getConfigurators(); // local reference
    doOverrideUrl(localAppDynamicConfigurators);

    if (serviceConfigurationListener != null) {
        List<Configurator> localDynamicConfigurators = serviceConfigurationListener.getConfigurators(); // local reference
        doOverrideUrl(localDynamicConfigurators);
    }
}

private void refreshInvoker(List<URL> invokerUrls) {
    Assert.notNull(invokerUrls, "invokerUrls should not be null");

    if (invokerUrls.size() == 1 && invokerUrls.get(0) != null && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
        this.forbidden = true; // Forbid to access
        this.invokers = Collections.emptyList();
        routerChain.setInvokers(this.invokers);
        destroyAllInvokers(); // Close all invokers
    } else {
        this.forbidden = false; // Allow to access
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls == Collections.<URL>emptyList()) {
            invokerUrls = new ArrayList<>();
        }
        if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<>();
            this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
        }
        if (invokerUrls.isEmpty()) {
            return;
        }
        // 這里會先按Protocol進行過濾,并且呼叫DubboProtocol.refer方法得到DubboInvoker
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map

        /**
         * If the calculation is wrong, it is not processed.
         *
         * 1. The protocol configured by the client is inconsistent with the protocol of the server.
         *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
         * 2. The registration center is not robust and pushes illegal specification data.
         *
         */
        if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
            logger.error(...);
            return;
        }

        List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
        // pre-route and build cache, notice that route cache should build on original Invoker list.
        // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
        // 得到了所引入的服務Invoker之后,把它們設定到路由鏈中去,在呼叫時使用,并且會呼叫TagRouter的notify方法
        routerChain.setInvokers(newInvokers);
        this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
        this.urlInvokerMap = newUrlInvokerMap;

        try {
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
    if (urls == null || urls.isEmpty()) {
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<>();
    String queryProtocols = this.queryMap.get(PROTOCOL_KEY);

    // 遍歷當前服務所有的服務提供者URL
    for (URL providerUrl : urls) {
        // If protocol is configured at the reference side, only the matching protocol is selected
        if (queryProtocols != null && queryProtocols.length() > 0) {
            boolean accept = false;
            String[] acceptProtocols = queryProtocols.split(",");

            // 當前消費者如果手動配置了Protocol,那么則進行匹配
            for (String acceptProtocol : acceptProtocols) {
                if (providerUrl.getProtocol().equals(acceptProtocol)) {
                    accept = true;
                    break;
                }
            }
            if (!accept) {
                continue;
            }
        }
        if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }

        // 當前Protocol是否在應用中存在對應的擴展點
        if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            logger.error(...);
            continue;
        }

        URL url = mergeUrl(providerUrl);

        String key = url.toFullString(); // The parameter urls are sorted
        if (keys.contains(key)) { // Repeated url
            continue;
        }
        keys.add(key);
        // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
        Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);

        // 如果當前服務提供者URL沒有生產過Invoker
        if (invoker == null) { // Not in the cache, refer again
            try {
                boolean enabled = true;
                if (url.hasParameter(DISABLED_KEY)) {
                    enabled = !url.getParameter(DISABLED_KEY, false);
                } else {
                    enabled = url.getParameter(ENABLED_KEY, true);
                }
                if (enabled) {
                    // 呼叫Protocol的refer方法得到一個Invoker
                    invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
                }
            } catch (Throwable t) {
                logger.error(...);
            }
            if (invoker != null) { // Put new invoker in cache
                newUrlInvokerMap.put(key, invoker);
            }
        } else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/516403.html

標籤:其他

上一篇:【Leetcode940】不同的子序列 II

下一篇:Redis常見問題和性能監控

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more