dubbo系列之spring boot核心配置讀取(三)
版本說明
springboot starter : 0.1.1
dubbo版本: 2.6.2
自動配置類
@Configuration
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true, havingValue = "true")
@ConditionalOnClass(AbstractConfig.class)
public class DubboAutoConfiguration {
// 單個dubbo配置繫結bean , 預設就是單個
@EnableDubboConfig
protected static class SingleDubboConfigConfiguration {
}
/**
* 多個dubbo配置繫結bean , 預設不使用。
*
*/
@ConditionalOnProperty(name = MULTIPLE_CONFIG_PROPERTY_NAME, havingValue = "true")
@EnableDubboConfig(multiple = true)
protected static class MultipleDubboConfigConfiguration {
}
/**
* service類,服務提供者的BeanDefinitionRegistryPostProcessor類,用來解析
* @Service註解,生成Service的BeanDefinition類,放入spring容器,供spring容器生成Bean
*
*/
@ConditionalOnProperty(name = BASE_PACKAGES_PROPERTY_NAME)
@ConditionalOnClass(RelaxedPropertyResolver.class)
@Bean
public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(Environment environment) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment);
Set<String> packagesToScan = resolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
return new ServiceAnnotationBeanPostProcessor(packagesToScan);
}
// springboot dataBinder 機制的擴充套件,用來將具體的屬性設定到相應的實體類裡面去。
@ConditionalOnClass(RelaxedDataBinder.class)
@Bean
@Scope(scopeName = SCOPE_PROTOTYPE)
public RelaxedDubboConfigBinder relaxedDubboConfigBinder() {
return new RelaxedDubboConfigBinder();
}
/**
* 用來解析@Reference 註解,消費者引用哪些服務,通過這個註解來進行引用
* 給標註這個@Reference註解的屬性賦值, 和@autowired的做法類似。
*
*/
@ConditionalOnMissingBean
@Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME)
public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() {
return new ReferenceAnnotationBeanPostProcessor();
}
}
配置說明:
SingleDubboConfigConfiguration : 引入了單個dubbo配置繫結bean的配置 , 預設使用
// 配置如下
dubbo.application
dubbo.module
dubbo.registry
dubbo.protocol
dubbo.monitor
dubbo.provider
dubbo.consumer
**MultipleDubboConfigConfiguration ** :多個dubbo配置繫結bean , 預設不使用。Dubbo @Service
和 @Reference
允許 Dubbo 應用關聯ApplicationConfig
Bean 或者指定多個RegistryConfig
Bean 等能力。換句話說,Dubbo 應用上下文中可能存在多個ApplicationConfig
等 Bean定義。
// 配置如下
dubbo.applications
dubbo.modules
dubbo.registries
dubbo.protocols
dubbo.monitors
dubbo.providers
dubbo.consumers
**serviceAnnotationBeanPostProcessor ** :解析service類註解的類,如果在spring boot啟動類上配置了@DubboComponentScan
則預設不使用。
**referenceAnnotationBeanPostProcessor ** : 為@Reference
注入物件,如果在spring boot啟動類上配置了@DubboComponentScan
則預設不使用。
因為在@DubboComponentScan
這個註解中引入了DubboComponentScanRegistrar
這個註冊類,該類中做了解析@service
註解和@Reference
的事情
@EnableDubboConfig
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationSelector.class) // 主要作用是這個
public @interface EnableDubboConfig {
/**
* It indicates whether binding to multiple Spring Beans.
*
* @return the default value is <code>false</code>
* @revised 2.5.9
*/
boolean multiple() default false;
}
主要的作用就是匯入了這個類DubboConfigConfigurationSelector
DubboConfigConfigurationSelector
public class DubboConfigConfigurationSelector implements ImportSelector, Ordered {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 獲取註解上的屬性,這個是通過@EnableDubboConfig匯入的,所以AnnotationMetadata裡面就包含了這個註解的值
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
// 是否是多配置,預設為false
boolean multiple = attributes.getBoolean("multiple");
if (multiple) {
return of(DubboConfigConfiguration.Multiple.class.getName());
} else {
// 這裡就直接講解單配置的。
return of(DubboConfigConfiguration.Single.class.getName());
}
}
private static <T> T[] of(T... values) {
return values;
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
DubboConfigConfigurationSelector
這個類實現了ImportSelector
介面,該介面的selectImports方法就是返回bean的名稱,供spring初始化,所以這裡返回了
DubboConfigConfiguration.Single.class.getName() , spring就會初始化這個類了。
Single
DubboConfigConfiguration.Single
的程式碼如下 , 通過@EnableDubboConfigBindings註解,匯入了多個@EnableDubboConfigBinding
@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)
})
public static class Single {
}
EnableDubboConfigBindings
由上面可以看到,spring在初始化Single這個類的時候,必然會載入他上面的註解,該類的主要作用就是為了匯入它上面的註解,@EnableDubboConfigBindings
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class) // 匯入了這個類。
public @interface EnableDubboConfigBindings {
/**
* The value of {@link EnableDubboConfigBindings}
*
* @return non-null
*/
EnableDubboConfigBinding[] value();
}
@EnableDubboConfigBindings
註解匯入了DubboConfigBindingsRegistrar這個類,該類的作用是將配置屬性和dubbo的配置進行繫結。註解的value是7個子註解
@EnableDubboConfigBinding
,後面DubboConfigBindingsRegistrar解析的時候,會獲取到這個7個子註解,將對應的屬性和dubbo的配置類進行繫結。
DubboConfigBindingsRegistrar
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 獲取匯入此類的註解資訊,@EnableDubboConfigBindings
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
// 獲取@EnableDubboConfigBinding 子註解
AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
// 初始化DubboConfigBindingRegistrar類,該類的主要作用就是為了解析單個的@EnableDubboConfigBinding註解
DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
registrar.setEnvironment(environment);
for (AnnotationAttributes element : annotationAttributes) {
// 迴圈註冊,通過註解裡面的資訊,生成Dubbo配置的BeanDefinition,最後放入spring容器中,供spring容器例項化。
registrar.registerBeanDefinitions(element, registry);
}
}
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment) environment;
}
}
註冊dubbo的配置bean
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
//1. 從環境 中取出 響應的屬性名
String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
// 2. 獲取dubbo的配置類的class
Class<? extends AbstractConfig> configClass = attributes.getClass("type");
// 3. 獲取是否是多個dubbo的配置
boolean multiple = attributes.getBoolean("multiple");
// 註冊dubbo的配置bean
registerDubboConfigBeans(prefix, configClass, multiple, registry);
}
步驟說明:
1.引數attributes就是@EnableDubboConfigBinding裡面的屬性,獲取prefix屬性值,就是獲取到了:dubbo.application
例:
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)
2.獲取dubbo的配置類的class,也就是獲取到了ApplicationConfig.class
3.獲取multiple的值,預設沒有配置就是false
4.呼叫registerDubboConfigBeans方法生成dubbo的配置bean
private void registerDubboConfigBeans(String prefix,
Class<? extends AbstractConfig> configClass,
boolean multiple,
BeanDefinitionRegistry registry) {
// 根據屬性名,如:dubbo.application 獲取具體的屬性值
Map<String, String> properties = getSubProperties(environment.getPropertySources(), prefix);
if (CollectionUtils.isEmpty(properties)) {
// 如果沒有配置,則沒有必要生成對應的dubbo配置bean了
if (log.isDebugEnabled()) {
log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
+ "] within prefix [" + prefix + "]");
}
return;
}
// BeanName
Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
Collections.singleton(resolveSingleBeanName(properties, configClass, registry));
for (String beanName : beanNames) {
// 生成bena
registerDubboConfigBean(beanName, configClass, registry);
// 註冊dubbo的DubboConfigBindingBeanPostProcessor
registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
}
}
private void registerDubboConfigBean(String beanName, Class<? extends AbstractConfig> configClass,
BeanDefinitionRegistry registry) {
// 生成BeanDefinitionBuilder
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
// 通過BeanDefinitionRegistry註冊dubbo的配置bean
registry.registerBeanDefinition(beanName, beanDefinition);
if (log.isInfoEnabled()) {
log.info("The dubbo config bean definition [name : " + beanName + ", class : " + configClass.getName() +
"] has been registered.");
}
}
DubboConfigBindingBeanPostProcessor
這個類是在dubbo的配置類初始化完成後會執行響應的方法。用來將屬性值設定到對應的屬性裡面去,在springboot中我們存在這種情況
first-name
,firstName
, FIRST_NAME
, 比如我們在yaml檔案中配置這樣的屬性,我們的java bean中的屬性是firstName , 在使用@ConfigurationProperties
註解的時候我們無需擔心,如果不是用springboot自身的config類來注入,那麼我們自己處理這種情況就會變的非常麻煩,所以dubbo選擇的是通過RelaxedDataBinder類來處理這個問題。這是spring boot的機制。
DubboConfigBindingBeanPostProcessor類實現了BeanPostProcessor
,ApplicationContextAware
, InitializingBean
這三個介面,下面是挑了一些重要的方法展示出來 , **每個dubbo配置類都有相應的DubboConfigBindingBeanPostProcessor **
public DubboConfigBindingBeanPostProcessor(String prefix, String beanName) {
Assert.notNull(prefix, "The prefix of Configuration Properties must not be null");
Assert.notNull(beanName, "The name of bean must not be null");
this.prefix = prefix; // 屬性字首
this.beanName = beanName; // dubbo的配置類名
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 會在每一個bean例項化之後、初始化(如afterPropertiesSet方法)之前被呼叫。
if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {
AbstractConfig dubboConfig = (AbstractConfig) bean;
// 將屬性和配置進行繫結
dubboConfigBinder.bind(prefix, dubboConfig);
if (log.isInfoEnabled()) {
log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
"configuration properties : " + prefix);
}
}
return bean;
}
@Override
public void afterPropertiesSet() throws Exception {
// DubboConfigBindingBeanPostProcessor 初始化之後就會執行
if (dubboConfigBinder == null) {
try {
// 從容器中獲取DubboConfigBinder , DubboConfigBinder的作用範圍是prototype , 每次呼叫getbean都會新建立一個
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);
}
dubboConfigBinder的程式碼如下,下面主要就是呼叫springboot 的dataBinder機制進行屬性設值了
public class RelaxedDubboConfigBinder extends AbstractDubboConfigBinder {
@Override
public <C extends AbstractConfig> void bind(String prefix, C dubboConfig) {
RelaxedDataBinder relaxedDataBinder = new RelaxedDataBinder(dubboConfig);
// Set ignored*
relaxedDataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields());
relaxedDataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields());
//從Environment中獲取屬性
Map<String, String> properties = getSubProperties(getPropertySources(), prefix);
// 將屬性MAP轉換為MutablePropertyValues
MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
// 繫結
relaxedDataBinder.bind(propertyValues);
}
}
通過上面的原始碼,可以看出來,dubbo的配置是一環接著一環,很多時候一個不起眼的地方就是往下走的關鍵程式碼,他主要是通過註解的匯入配置類,然後通過
BeanDefinitionRegistry生成對應的beanDefintion放入spring容器中。
本文所解析的這些原始碼均不涉及dubbo的核心功能,僅僅是講了dubbo啟動之後,如何獲取到配置,如果進行配置裝配,方便大家後續有個好的理解。
有興趣可以看下一spring的擴充套件機制,dubbo中都有大量的使用到。