В этой статье в основном представлен принцип интеграции 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Триггеры аннотаций. Но это не кажется очень полезным.