Весенняя интеграция исходного кода Dubbo

Dubbo

В этой статье в основном представлен принцип интеграции dubbo и spring в различных режимах конфигурации, а именно: конфигурация xml, конфигурация аннотаций, автоматическая конфигурация, эффективный принцип конфигурации в трех режимах.

XML-запуск

Механизм расширения схемы

Spring предоставляет механизм расширения схемы, пользователи могут настраивать файл схемы и анализатор схемы, а затем интегрировать его в контейнер SpringIOC. Чтобы создать пользовательское расширение, в основном нужно выполнить следующие шаги:

  1. Создайте файл схемы, описывающий пользовательские юридические стандартные блоки, то есть файлы xsd, которые в основном используются для определения ограничений данных;
  2. Настройте класс обработчика, реализуйте интерфейс NamespaceHandler и зарегистрируйте BeanDefinitionParser, соответствующий каждой метке в нем;
  3. Настройте один или несколько синтаксических анализаторов и реализуйте интерфейс BeanDefinitionParser для определения логики синтаксического анализа bean-компонентов;

Процесс разбора

Для получения подробной информации о реализации Spring этой части вы можете обратиться кРазбор схемы, ниже я сделаю простое расчесывание этой части:

  1. Разбор bean-компонентов в Spring в основном осуществляется черезDefaultBeanDefinitionDocumentReader#parseBeanDefinitionsметод, конкретная логика синтаксического анализа делегируетсяBeanDefinitionParserDelegateпровести;
  2. DefaultBeanDefinitionDocumentReader#parseBeanDefinitionsБудет различать пространство имен по умолчанию и пользовательское пространство имен (за исключением некоторых тегов Spring по умолчанию, остальные являются пользовательскими пространствами имен).
  3. Вызывается при анализе пользовательского пространства именDefaultNamespaceHandlerResolver#resolveметод,DefaultNamespaceHandlerResolverзагрузит всеMETA-INF/spring.handlersСодержимое файла, а затем сохранить наборNamespaceURL => NamespaceHandlerкартографические отношения. затем вDefaultNamespaceHandlerResolver#resolveВ методе вызывается соответствующий URL-адрес текущего NamespaceURLNamespaceHandler#initметод.
  4. Обработчик NamespaceHandler, соответствующий dubbo,DubboNamespaceHandler,существуетDubboNamespaceHandler#initВ методе будет найден интерфейс BeanDefinitionParser, соответствующий каждой метке, что соответствуетDubboBeanDefinitionParserи кешируется;
  5. Вызывается при разборе теговDubboNamespaceHandler#parseметод, в то время как реальная логика разбора делегирована внутреннемуDubboBeanDefinitionParser#parseметод;

Дополнительная часть кода ключа:

// DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    // 默认解析
                    parseDefaultElement(ele, delegate);
                }else {
                    // 自定义解析
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        // 自定义解析
        delegate.parseCustomElement(root);
    }
}
// DubboNamespaceHandler.java
// NamespaceHandlerSupport是一个抽象类,实现了NamespaceHandler接口
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

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

}

На этом этапе весь процесс анализа xml-тегов dubbo очень ясен.Если вы хотите использовать dubbo через XML-конфигурацию, после настройки xml при запуске Spring теги, соответствующие dubbo, будут автоматически проанализированы.

Начало аннотации

Аннотация состоит в том, чтобы позволить нам избавиться от громоздкой конфигурации XML, но она навязчива к коду.Интеграция высокой версии dubbo и springboot на самом деле очень удобна.После введения зависимости вам нужно только добавить ее в стартовый класс.@EnableDubboПросто аннотируйте. кdubbo 2.7.2а такжеspringboot 2.1.4.RELEASEНапример:

Пример использования

полагаться

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'

    implementation 'org.apache.dubbo:dubbo:2.7.2'
    implementation 'org.apache.dubbo:dubbo-registry-zookeeper:2.7.2'
    implementation 'org.apache.dubbo:dubbo-metadata-report-zookeeper:2.7.2'

    implementation 'org.apache.zookeeper:zookeeper:3.4.12'
    implementation 'org.apache.curator:curator-recipes:2.12.0'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

стартовый класс


@SpringBootApplication
@EnableDubbo
public class SDubboApplication {

    public static void main(String[] args) {
        SpringApplication.run(SDubboApplication.class, args);
    }

    @Configuration
    @PropertySource("classpath:/dubbo-provider.properties")
    static class ProviderConfiguration {
        @Bean
        public RegistryConfig registryConfig() {
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://10.9.44.133:2181");

            // 注册简化版的的url到注册中心
            registryConfig.setSimplified(true);
            return registryConfig;
        }

        @Bean
        public MetadataReportConfig metadataReportConfig() {
            MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
            metadataReportConfig.setAddress("zookeeper://10.9.44.133:2181");
            return metadataReportConfig;
        }

        @Bean
        public ConfigCenterConfig configCenterConfig() {
            ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
            configCenterConfig.setAddress("zookeeper://10.9.44.133:2181");
            return configCenterConfig;
        }
    }

}

dubbo-provider.properties

dubbo.application.name=sdubbo
dubbo.protocol.name=dubbo
dubbo.protocol.port=20882

Введение в определение Provider и Consumer с помощью аннотаций отсутствует. Как вы можете видеть из приведенного выше кода, эти три bean-компонента — это всего лишь некоторая работа по настройке, это не находится в центре нашего внимания, основное внимание уделяется@EnableDubboАннотация, почему сервис dubbo автоматически регистрируется после добавления этой аннотации?

@EnableDubbo

Давайте сначала посмотрим на эту аннотацию, вы можете обнаружить, что она ссылается@EnableDubboConfigа также@DubboComponentScan, бывший и配置связанных, последний с服务注册和服务引用Связанный.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
......
}

@EnableDubboConfig

Эта аннотация и外部化配置相关, вы можете обратиться к записи в блоге:Внешняя конфигурация

То есть: некоторая общая информация о конфигурации настроена вapplication.propertiesилиbootstrap.propertiesВ файле конфигурации dubbo будет автоматически создан на основе этой информации о конфигурации.ApplicationConfig,RegistryConfig,ProviderConfigВ ожидании Beans нам не нужно создавать их, жестко кодируя их с помощью аннотаций.

Его основной принцип заключается вDubboConfigConfigurationRegistrarкласс, это не является предметом этой статьи, а скорее введение.

Фактически, в приведенном выше примере использовалась функция внешней конфигурации, хотя и не вapplication.yamlЭти свойства dubbo определены в , но передаются в классе аннотаций.@PropertySource("classpath:/dubbo-provider.properties")Эти свойства импортируются, поэтому dubbo автоматически создаст соответствующие bean-компоненты на основе этих свойств, таких какApplicationConfig, хотя в примере это не жестко закодированоApplicationConfig, но даббо читаетdubbo-provider.propertiesв файлеdubbo.applicationсвойство автоматически создастApplicationConfig.

Во внешней конфигурации dubbo и springboot интегрируются следующим образом:

полагаться

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'

    implementation 'org.apache.dubbo:dubbo:2.7.2'
    implementation 'org.apache.dubbo:dubbo-registry-zookeeper:2.7.2'
    implementation 'org.apache.dubbo:dubbo-metadata-report-zookeeper:2.7.2'

    implementation 'org.apache.zookeeper:zookeeper:3.4.12'
    implementation 'org.apache.curator:curator-recipes:2.12.0'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

application.yml

server:
  port: 8786
spring:
  main:
    allow-bean-definition-overriding: true

dubbo:
  application:
    name: sdubbo
  protocol:
    name: dubbo
    port: 20882
  registry:
    address: zookeeper://10.9.44.133:2181
    simplified: true
  metadata-report:
    address: zookeeper://10.9.44.133:2181
  config-center:
    address: zookeeper://10.9.44.133:2181

стартовый класс

@SpringBootApplication
@EnableDubbo
public class SDubboApplication {
    public static void main(String[] args) {
        SpringApplication.run(SDubboApplication.class, args);
    }
}

@DubboComponentScan

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

небольшое расширение

Весной через@ImportЕсть три способа импортировать внешний класс

  1. импортировать напрямую;
@Configuration
@Import(ExternalBean.class)
public class TestImportConfiguration {
}
  1. импортировать одинImportSelectorКласс реализации интерфейса, а затем переопределитьselectImportsметод, в котором возвращается полное имя импортируемого класса;
@Configuration
@Import(TestImportSelect.class)
public class TestImportSelectConfiguration {
}

public class TestImportSelect implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.sxy.spring.register.ExternalBean"};
    }
}
  1. импортировать одинImportBeanDefinitionRegistrarКласс реализации интерфейса, а затем переопределитьregisterBeanDefinitionsметод, при которомBeanDefinitionRegistryЗарегистрировать BeanDefinition;
@Configuration
@Import(TestImportBeanDefinitionRegistrar.class)
public class TestImportBeanDefinitionRegistrarCongiguration {
}

public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 注册一个bean, 指定bean name
        registry.registerBeanDefinition("externalBean", new RootBeanDefinition(ExternalBean.class));
    }
}

DubboComponentScanRegistrar

DubboComponentScanRegistrarДостигнутоImportBeanDefinitionRegistrarинтерфейс, перекрываяregisterBeanDefinitionsМетод делает две вещи:

  1. регистрServiceAnnotationBeanPostProcessor;
  2. регистрReferenceAnnotationBeanPostProcessor;

ServiceAnnotationBeanPostProcessorДостигнутоBeanDefinitionRegistryPostProcessorинтерфейс;ReferenceAnnotationBeanPostProcessorДостигнутоInstantiationAwareBeanPostProcessorAdapterинтерфейс. Студенты, знакомые со Spring, знают, что это интерфейс расширения Spring.

небольшое расширение

  1. BeanFactoryPostProcessor: Перед созданием экземпляра компонента вы можете изменить информацию BeanDefinition;
  2. BeanDefinitionRegistryPostProcessor: BeanFactoryPostProcessorПодкласс интерфейса, выполняемый до BeanFactoryPostProcessor, может использоваться для создания BeanDefinition;
  3. BeanPostProcessor: Выполняется до и после инициализации Бина.
  4. InstantiationAwareBeanPostProcessor: подкласс BeanPostProcessor, выполняемый до и после создания экземпляра;
  5. ApplicationContextAwareProcessor: реализует BeanPostProcessor и внедряет различные интерфейсы Aware в postProcessBeforeInitialization;
ServiceAnnotationBeanPostProcessor

Возьмем в качестве примера провайдера dubbo.

@org.apache.dubbo.config.annotation.Service
public class LeannImpl implements ILearn {
    @Override
    public String learn(String name) {
        return "学习: " + name;
    }
}

существуетServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistryметод, в основном сделать две вещи:

  1. В соответствии с настроенным путем сканирования пакетов найти все пакеты с@org.apache.dubbo.config.annotation.ServiceАннотированные классы, созданные для этих классовBeanDefinition, а затем зарегистрируйтесь в контейнере IOC; эта часть реализации скрыта вDubboClassPathBeanDefinitionScanner#scanметод.
  2. Создайте еще один для каждого исходного классаServiceBeanТипBeanDefinitionИнформация.

То есть: каждая служба dubbo в конечном итоге будет соответствовать двум bean-компонентам в контейнере IOC, один из которых является примитивным типом, а другой —ServiceBeanТипы,ServiceBeanНа самом делеFactoryBean, является ключом к реализации услуги, которая не будет здесь подробно останавливаться. Взяв приведенный выше пример в качестве примера, BeanNames, соответствующие последним двум bean-компонентам:leannImplа такжеServiceBean:com.sxy.sdubbo.service.ILearn

ReferenceAnnotationBeanPostProcessor

Возьмем в качестве примера потребителя dubbo.

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
    @Reference(timeout = 3000)
    private DemoService demoService;

    @Override
    public String sayHello(String name) {
        return demoService.sayHello(name);
    }
}

ReferenceAnnotationBeanPostProcessorДелайте в основном две вещи:

  1. Создать прокси-сервис;
  2. дляDemoServiceComponentввести значение, т.е.DemoServiceComponent.demoService = 代理服务;

который:DemoServiceComponentпросто представляет собой обычный компонент в контейнере Spring; и@Reference注解标注demoService属性Наконец, он указывает на динамически создаваемую прокси-службу, которая должна общаться с провайдером через эту прокси-службу.

На данный момент в режиме аннотации процесс регистрации и ссылки на сервис dubbo очень понятен, а конкретные детали реализации можно найти в исходном коде.

Автоматическая конфигурация

Автоматическая конфигурация на самом деле является функцией, предоставляемой springboot. Ее цель — попытаться заставить пользователей реализовать различные громоздкие конфигурации. Основной принцип — прочитать классы автоматической конфигурации в META-INF/spring.factories. Ниже приводится краткое введение.

Автоматическая конфигурация Spring

При запуске приложения Springboot оно добавит@SpringBootApplicationаннотацию, содержащую@EnableAutoConfigurationаннотация, в то время как@EnableAutoConfigurationявляется ключом к автоматической настройке

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}

ядроAutoConfigurationImportSelectorкласс, реализующийImportSelectorИнтерфейс, по сути, в основном делает одну вещь:

  1. пройти черезSpringFactoriesLoader#loadFactoriesМетод загружает файл META-INF/spring.factories всех файлов JAR в пути к классам, а затем извлекает все файлы в файле.xxxEnableAutoConfiguration, что эквивалентно преобразованию всехxxxEnableAutoConfigurationЗарегистрирован в контейнере Spring. Конечно, этиxxxEnableAutoConfigurationОбычно это комбинация@Conditionalчтобы определить, создавать ли Bean.

dubbo-spring-boot-starter

Проект Springboot состоит из стартеров. Стартер обычно содержит зависимости, необходимые модулю. Обычно в стартере также выполняется автоматическая настройка.

Автоматическая настройка dubbo в приложении springboot также выполняется через стартер, официальный адрес Git:dubbo-spring-boot-project

Итак, в режиме автоматической настройки, что нужно сделать для интеграции dubbo и springboot? Его можно настроить с помощью экстернализации или аннотации.Вот пример настройки с помощью аннотации:

полагаться

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    
    // 因为目前(2019/07/19)dubbo-spring-boot-starter最新只有2.7.1版本
    implementation 'org.apache.dubbo:dubbo-spring-boot-starter:2.7.1'
    implementation 'org.apache.dubbo:dubbo:2.7.2'
    implementation 'org.apache.dubbo:dubbo-registry-zookeeper:2.7.2'
    implementation 'org.apache.dubbo:dubbo-metadata-report-zookeeper:2.7.2'

    implementation 'org.apache.zookeeper:zookeeper:3.4.12'
    implementation 'org.apache.curator:curator-recipes:2.12.0'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

стартовый класс

// @EnableDubbo 不需要加这个注解
@SpringBootApplication
public class SDubboApplication {
    public static void main(String[] args) {
        SpringApplication.run(SDubboApplication.class, args);
    }

    @Configuration
    @PropertySource("classpath:/dubbo-provider.properties")
    static class ProviderConfiguration {
        @Bean
        public RegistryConfig registryConfig() {
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://10.9.44.133:2181");

            // 注册简化版的的url到注册中心
            registryConfig.setSimplified(true);
            return registryConfig;
        }
        @Bean
        public MetadataReportConfig metadataReportConfig() {
            MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
            metadataReportConfig.setAddress("zookeeper://10.9.44.133:2181");
            return metadataReportConfig;
        }
        @Bean
        public ConfigCenterConfig configCenterConfig() {
            ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
            configCenterConfig.setAddress("zookeeper://10.9.44.133:2181");
            return configCenterConfig;
        }
    }
}

dubbo-provider.properties

dubbo.application.name=sdubbo
dubbo.protocol.name=dubbo
dubbo.protocol.port=20882
# 多了一个包扫描,在当前 starter 版本中,这个属性必须配置
dubbo.scan.base-packages =com.sxy.sdubbo

Разница с методом аннотации:

  1. Добавить кdubbo-spring-boot-starterполагаться;
  2. Настройка не требуется@EnableDubboаннотация;
  3. Необходимо настроить путь сканирования пакетаdubbo.scan.base-packages;

Принцип реализации является автоматической конфигурацией в Springboot:dubbo-spring-boot-starterФайл pom.xml представляетdubbo-spring-boot-autoconfigureмодуль,dubbo-spring-boot-autoconfigureФайл pom.xml представляетdubbo-spring-boot-autoconfigure-compatibleмодуль;

  1. существуетdubbo-spring-boot-autoconfigureВ модуле содержимое файла META-INF/spring.factories выглядит следующим образом:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration
  1. существуетdubbo-spring-boot-autoconfigure-compatibleВ модуле содержимое файла META-INF/spring.factories выглядит следующим образом:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration
org.springframework.context.ApplicationListener=\
org.apache.dubbo.spring.boot.context.event.OverrideDubboConfigApplicationListener,\
org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener,\
org.apache.dubbo.spring.boot.context.event.AwaitingNonWebApplicationListener
org.springframework.boot.env.EnvironmentPostProcessor=\
org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessor
org.springframework.context.ApplicationContextInitializer=\
org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer

DubboRelaxedBindingAutoConfigurationОн в основном связан с анализом атрибутов и не будет здесь представлен; ядро ​​по-прежнемуDubboAutoConfiguration, В этом классе есть два ключевых фрагмента кода.

/**
 * @ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME) 强制要求了我们需要配置包扫描路径,否则该Bean不会被创建
 * Creates {@link ServiceAnnotationBeanPostProcessor} Bean
 *
 * @param propertyResolver {@link PropertyResolver} Bean
 * @return {@link ServiceAnnotationBeanPostProcessor}
 */
@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
@ConditionalOnBean(name = BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME)
@Bean
public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(
        @Qualifier(BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME) PropertyResolver propertyResolver) {
    Set<String> packagesToScan = propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
    return new ServiceAnnotationBeanPostProcessor(packagesToScan);
}

/**
 * Creates {@link ReferenceAnnotationBeanPostProcessor} Bean if Absent bean工厂不存在referenceAnnotationBeanPostProcessor时创建
 *
 * @return {@link ReferenceAnnotationBeanPostProcessor}
 */
@ConditionalOnMissingBean
@Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME)
public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() {
    return new ReferenceAnnotationBeanPostProcessor();
}

На самом деле ядро ​​создается за счет автоматизацииServiceAnnotationBeanPostProcessorа такжеReferenceAnnotationBeanPostProcessor, не прошел@EnableDubboТриггеры аннотаций. Но это не кажется очень полезным.