前言:
最近研究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类的注入。