В этой статье в основном представлен принцип интеграции dubbo и spring в различных режимах конфигурации, а именно: конфигурация xml, конфигурация аннотаций, автоматическая конфигурация, эффективный принцип конфигурации в трех режимах.
XML-запуск
Механизм расширения схемы
Spring предоставляет механизм расширения схемы, пользователи могут настраивать файл схемы и анализатор схемы, а затем интегрировать его в контейнер SpringIOC. Чтобы создать пользовательское расширение, в основном нужно выполнить следующие шаги:
- Создайте файл схемы, описывающий пользовательские юридические стандартные блоки, то есть файлы xsd, которые в основном используются для определения ограничений данных;
- Настройте класс обработчика, реализуйте интерфейс NamespaceHandler и зарегистрируйте BeanDefinitionParser, соответствующий каждой метке в нем;
- Настройте один или несколько синтаксических анализаторов и реализуйте интерфейс BeanDefinitionParser для определения логики синтаксического анализа bean-компонентов;
Процесс разбора
Для получения подробной информации о реализации Spring этой части вы можете обратиться кРазбор схемы, ниже я сделаю простое расчесывание этой части:
- Разбор bean-компонентов в Spring в основном осуществляется через
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
метод, конкретная логика синтаксического анализа делегируетсяBeanDefinitionParserDelegate
провести; -
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
Будет различать пространство имен по умолчанию и пользовательское пространство имен (за исключением некоторых тегов Spring по умолчанию, остальные являются пользовательскими пространствами имен). - Вызывается при анализе пользовательского пространства имен
DefaultNamespaceHandlerResolver#resolve
метод,DefaultNamespaceHandlerResolver
загрузит всеMETA-INF/spring.handlers
Содержимое файла, а затем сохранить наборNamespaceURL => NamespaceHandler
картографические отношения. затем вDefaultNamespaceHandlerResolver#resolve
В методе вызывается соответствующий URL-адрес текущего NamespaceURLNamespaceHandler#init
метод. - Обработчик NamespaceHandler, соответствующий dubbo,
DubboNamespaceHandler
,существуетDubboNamespaceHandler#init
В методе будет найден интерфейс BeanDefinitionParser, соответствующий каждой метке, что соответствуетDubboBeanDefinitionParser
и кешируется; - Вызывается при разборе тегов
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
Есть три способа импортировать внешний класс
- импортировать напрямую;
@Configuration
@Import(ExternalBean.class)
public class TestImportConfiguration {
}
- импортировать один
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"};
}
}
- импортировать один
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
Метод делает две вещи:
- регистр
ServiceAnnotationBeanPostProcessor
; - регистр
ReferenceAnnotationBeanPostProcessor
;
ServiceAnnotationBeanPostProcessor
ДостигнутоBeanDefinitionRegistryPostProcessor
интерфейс;ReferenceAnnotationBeanPostProcessor
ДостигнутоInstantiationAwareBeanPostProcessorAdapter
интерфейс. Студенты, знакомые со Spring, знают, что это интерфейс расширения Spring.
небольшое расширение
-
BeanFactoryPostProcessor
: Перед созданием экземпляра компонента вы можете изменить информацию BeanDefinition; -
BeanDefinitionRegistryPostProcessor
:BeanFactoryPostProcessor
Подкласс интерфейса, выполняемый до BeanFactoryPostProcessor, может использоваться для создания BeanDefinition; -
BeanPostProcessor
: Выполняется до и после инициализации Бина. -
InstantiationAwareBeanPostProcessor
: подкласс BeanPostProcessor, выполняемый до и после создания экземпляра; -
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
метод, в основном сделать две вещи:
- В соответствии с настроенным путем сканирования пакетов найти все пакеты с
@org.apache.dubbo.config.annotation.Service
Аннотированные классы, созданные для этих классовBeanDefinition
, а затем зарегистрируйтесь в контейнере IOC; эта часть реализации скрыта вDubboClassPathBeanDefinitionScanner#scan
метод. - Создайте еще один для каждого исходного класса
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
Делайте в основном две вещи:
- Создать прокси-сервис;
- для
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
Интерфейс, по сути, в основном делает одну вещь:
- пройти через
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
Разница с методом аннотации:
- Добавить к
dubbo-spring-boot-starter
полагаться; - Настройка не требуется
@EnableDubbo
аннотация; - Необходимо настроить путь сканирования пакета
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
модуль;
- существует
dubbo-spring-boot-autoconfigure
В модуле содержимое файла META-INF/spring.factories выглядит следующим образом:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration
- существует
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
Триггеры аннотаций. Но это не кажется очень полезным.