前言:
最近研究Zookeeper以及Dubbo,在使用Dubbo時有配置文件以及註解的兩種方式,爲了圖方便使用了註解的方式,但是爆出了空指針的問題,分析才發現自己沒有加@EnableDubbo註解,於是想研究下@EnableDubbo是如何實現配置文件功能的。
從源碼分析@EnableDubbo
的作用
1. 查看@EnableDubbo
/**
* Enables Dubbo components as Spring Beans, equals
* {@link DubboComponentScan} and {@link EnableDubboConfig} combination.
* <p>
* Note : {@link EnableDubbo} must base on Spring Framework 4.2 and above
*
* @see DubboComponentScan
* @see EnableDubboConfig
* @since 2.5.8
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
......
}
在源碼中我們可以看到@EnableDubbo
是@EnableDubboConfig
以及@DubboComponentScan
合併的一個註解。
EnableDubboConfig
:實現Dubbo的配置,功能類似與xml配置文件中的各種配置,在該註解中可以實現類似的功能,通過綁定的方式來實現。DubboComponentScan
:掃描Dubbo的組件類,將Dubbo的組件類自動注入到Spring容器中。
2. 查看@EnableDubboConfig
註解
2.1、首先查看註釋,可以通過註釋看到,各種配置類綁定了對應的配置文件
中的配置功能。
- ApplicationConfig 綁定 dubbo.application
- ModuleConfig 綁定 dubbo.module
- …
通過這些註釋我們大概知道各種配置類
要實現的Dubbo
的功能,通過讀取Dubbo服務的相關類,將這些相關類設置相應的Dubbo
配置。
/**
* As a convenient and multiple {@link EnableDubboConfigBinding}
* in default behavior , is equal to single bean bindings with below convention prefixes of properties:
* <ul>
* <li>{@link ApplicationConfig} binding to property : "dubbo.application"</li>
* <li>{@link ModuleConfig} binding to property : "dubbo.module"</li>
* <li>{@link RegistryConfig} binding to property : "dubbo.registry"</li>
* <li>{@link ProtocolConfig} binding to property : "dubbo.protocol"</li>
* <li>{@link MonitorConfig} binding to property : "dubbo.monitor"</li>
* <li>{@link ProviderConfig} binding to property : "dubbo.provider"</li>
* <li>{@link ConsumerConfig} binding to property : "dubbo.consumer"</li>
* </ul>
* <p>
* In contrast, on multiple bean bindings that requires to set {@link #multiple()} to be <code>true</code> :
* <ul>
* <li>{@link ApplicationConfig} binding to property : "dubbo.applications"</li>
* <li>{@link ModuleConfig} binding to property : "dubbo.modules"</li>
* <li>{@link RegistryConfig} binding to property : "dubbo.registries"</li>
* <li>{@link ProtocolConfig} binding to property : "dubbo.protocols"</li>
* <li>{@link MonitorConfig} binding to property : "dubbo.monitors"</li>
* <li>{@link ProviderConfig} binding to property : "dubbo.providers"</li>
* <li>{@link ConsumerConfig} binding to property : "dubbo.consumers"</li>
* </ul>
*
* @see EnableDubboConfigBinding
* @see DubboConfigConfiguration
* @see DubboConfigConfigurationRegistrar
* @since 2.5.8
*/
2.2、查看具體配置
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.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 true;
}
在該註解中可以看到通過Import導入了一個DubboConfigConfigurationRegistrar.class
的類
3、查看DubboConfigConfigurationRegistrar
類
/**
* Dubbo {@link AbstractConfig Config} {@link ImportBeanDefinitionRegistrar register}, which order can be configured
*
* @see EnableDubboConfig
* @see DubboConfigConfiguration
* @see Ordered
* @since 2.5.8
*/
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
// 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);
}
// Since 2.7.6
registerCommonBeans(registry);
}
}
在該類中主要實現的是註冊Dubbo的相關類,通過registerBeans
來實現。
同時我們在這段源碼中可以看到通過 multiple
判斷是否爲單例,如果爲單例則調用DubboConfigConfiguration.Single.class
,如果爲多對象則調用DubboConfigConfiguration.Multiple.class
4、進入DubboConfigConfiguration.Single.class
類
在該類中我們可以看到通過各種配置類ApplicationConfig.class、ModuleConfig.class、.....
來實現了xml配置文件
中的各種配置
public class DubboConfigConfiguration {
/**
* Single Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableConfigurationBeanBindings({
@EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
})
public static class Single {
}
/**
* Multiple Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableConfigurationBeanBindings({
@EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {
}
}
5、@DubboComponentScan
的源碼分析
5.1、DubboComponentScan 解析
/**
* Dubbo Component Scan {@link Annotation},scans the classpath for annotated components that will be auto-registered as
* Spring beans. Dubbo-provided {@link Service} and {@link Reference}.
*
* @see Service
* @see Reference
* @since 2.5.7
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
........
}
在該源碼中可以通過註釋看到,通過路徑掃描到的Dubbo組件,將會自動注入到Spring中。通過@Service
以及@Reference
實現注入。
5.2、DubboComponentScanRegistrar
解析
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans
registerCommonBeans(registry);
}
/**
* Registers {@link ServiceAnnotationBeanPostProcessor}
*
* @param packagesToScan packages to scan without resolving placeholders
* @param registry {@link BeanDefinitionRegistry}
* @since 2.5.8
*/
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
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;
}
}
5.3、查看registerCommonBeans
類
public interface DubboBeanUtils {
/**
* Register the common beans
*
* @param registry {@link BeanDefinitionRegistry}
* @see ReferenceAnnotationBeanPostProcessor
* @see DubboConfigDefaultPropertyValueBeanPostProcessor
* @see DubboConfigAliasPostProcessor
* @see DubboLifecycleComponentApplicationListener
* @see DubboBootstrapApplicationListener
*/
static void registerCommonBeans(BeanDefinitionRegistry registry) {
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
// Since 2.7.5 Register DubboLifecycleComponentApplicationListener as an infrastructure Bean
registerInfrastructureBean(registry, DubboLifecycleComponentApplicationListener.BEAN_NAME,
DubboLifecycleComponentApplicationListener.class);
// Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
DubboBootstrapApplicationListener.class);
// Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
}
}
在該類中看第一個方法,通過註釋大概可以知道註解@Reference
實現注入類,進入ReferenceAnnotationBeanPostProcessor.class
類,
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that Consumer service {@link Reference} annotated fields
*
* @since 2.5.7
*/
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
ApplicationContextAware, ApplicationListener<ServiceBeanExportedEvent> {
/**
* The bean name of {@link ReferenceAnnotationBeanPostProcessor}
*/
public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor";
/**
* Cache size
*/
private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32);
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedFieldReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedMethodReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap<String, ReferencedBeanInvocationHandler> referencedBeanInvocationHandlersCache =
new ConcurrentHashMap<>();
private ApplicationContext applicationContext;
.
.
.
}
在該類中具體實現了向Spring容器中注入的方法,在該方法中有創建了Map容器,以及Spring中的ApplicationContext
…通過以上來實現Dubbo類的注入。