一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
dubbo究竟是如何发布一个服务的?它是如何做到像调用本地方法一样调用远程服务的?从今天开始,dubbo源码分析系列的文章开始更新啦。今天我们通过解析相关的源码,理解dubbo是如何发布一个服务的。
我们分析dubbo注解方法发布服务的代码来加深dubbo的理解(xml方式本质上没什么区别,基于现在大部分都是基于注解开发)。dubbo版本:3.0.2.1
首先我们通过@EnableDubbo注解为入口进行分析。
@EnableDubbo
@EnableDubbo注解是@EnableDubboConfig和@DubboComponentScan两个注解的复合注解。 我们分别对两个注解进行分析。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
boolean multipleConfig() default true;
}
复制代码
@EnableDubboConfig
@EnableDubboConfig注解主要作用就是使用@Import注解Import了DubboConfigConfigurationRegistrar类
注:如果对@Import注解还不了解的同学,可以看我的另一篇文章@Import注解的使用和原理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
boolean multiple() default true;
}
复制代码
因为DubboConfigConfigurationRegistrar实现了ImportBeanDefinitionRegistrar类,那么在处理@Import注解的时候,会执行它的registerBeanDefinitions()方法。registerBeanDefinitions()会调用registerCommonBeans()方法注册一些基础的bean。
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registerCommonBeans(registry);
}
}
复制代码
registerCommonBeans()方法会调用registerInfrastructureBean()方法注册几个基础的bean到Spring容器中,这些bean我们之后进行分析。在服务的发布流程中DubboBootstrapApplicationListener这个bean很关键。
static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);
registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// TODO Whether DubboConfigAliasPostProcessor can be removed ?
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.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);
// Dubbo config initializer
registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
// register infra bean if not exists later
registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}
复制代码
registerInfrastructureBean()
registerInfrastructureBean()方法作用就是通过beanDefinitionRegistry将指定类型的bean加入到Spring的IoC容器中。
static boolean registerInfrastructureBean(BeanDefinitionRegistry beanDefinitionRegistry,
String beanName,
Class<?> beanType) {
boolean registered = false;
if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
registered = true;
if (log.isDebugEnabled()) {
log.debug("The Infrastructure bean definition [" + beanDefinition
+ "with name [" + beanName + "] has been registered.");
}
}
return registered;
}
复制代码
接下来看@DubboComponentScan注解
@DubboComponentScan
@DubboComponentScan注解的主要作用是通过@Import注解导入了DubboComponentScanRegistrar类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
复制代码
DubboComponentScanRegistrar类同样实现了ImportBeanDefinitionRegistrar类。所以也会执行它的registerBeanDefinitions()方法。registerBeanDefinitions()方法首先也会调用registerCommonBeans()方法去注册几个基础的bean,然后调用getPackagesToScan获取获取需要扫描的包,最后调用registerServiceAnnotationPostProcessor()方法注册一个ServiceAnnotationPostProcessor类型的bean到IoC容器中。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册基础的几个bean 前面分析过了 这里就不再赘述
registerCommonBeans(registry);
//获取需要扫描的包
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
//注册ServiceAnnotationPostProcessor bean
registerServiceAnnotationPostProcessor(packagesToScan, registry);
}
复制代码
我们首先看getPackagesToScan()方法
getPackagesToScan()
getPackagesToScan()方法获取需要扫描的包的集合。
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
//获取@DubboComponentScan注解的元数据
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
//获取@DubboComponentScan注解的basePackages
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;
}
复制代码
registerServiceAnnotationPostProcessor()
registerServiceAnnotationPostProcessor()的作用就是利用BeanDefinitionRegistry注册了一个类型为ServiceAnnotationPostProcessor的bean。
private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
复制代码
接下来我们看ServiceAnnotationPostProcessor这个bean做了什么
首先我们可以看到它实现了BeanDefinitionRegistryPostProcessor
接口,那在注册bean之后必然会执行postProcessBeanDefinitionRegistry()方法,所以我们看postProcessBeanDefinitionRegistry()方法做了什么。
postProcessBeanDefinitionRegistry()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
//首先解析packagesToScan 因为@DubboComponentScan注解指定扫描的包名是支持EL表达式的 解析出来正在需要扫描的包名保存起来
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
//解析指定包名下的@Service @DubboService注解
scanServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
复制代码
首先看resolvePackagesToScan()方法
resolvePackagesToScan()
resolvePackagesToScan()这个方法的作用主要是通过Spring的environment对象的resolvePlaceholders()方法对注解中的需要解析的占位符包名进行解析,获得真正的包名。
private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
for (String packageToScan : packagesToScan) {
if (StringUtils.hasText(packageToScan)) {
String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
resolvedPackagesToScan.add(resolvedPackageToScan);
}
}
return resolvedPackagesToScan;
}
复制代码
scanServiceBeans()
创建一个DubboClassPathBeanDefinitionScanner的scanner包扫描器,它继承了Spring的ClassPathBeanDefinitionScanner,也就是和复用了Spring的扫包逻辑,对@Service,@DubboService注解进行扫描,然后将扫描出来的bean放入到beanDefinitionHolders中,然后循环调用processScannedBeanDefinition()方法,注册对应的ServiceBean。所以结下来我们重点分析processScannedBeanDefinition()方法。
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
//bean名称生成器
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
//将根据@DubboService @Service注解过滤的过滤器加入到scanner扫描器中
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
// avoid duplicated scans
//解析过了之后就不要再解析了
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
if (logger.isInfoEnabled()) {
logger.info("Ignore package who has already bean scanned: " + packageToScan);
}
continue;
}
// Registers @Service Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);
}
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No class annotated by Dubbo @Service was found under package ["
+ packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
}
}
servicePackagesHolder.addScannedPackage(packageToScan);
}
}
复制代码
processScannedBeanDefinition()
这个方法的主要作用就是为每个服务注册一个ServiceBean的实例到Spring IoC容器中,首先获取服务实现类上的注解属性,以及接口的名称,根据接口名称,version属性,group属性生成serviceBean的name,然后通过buildServiceBeanDefinition()方法生成ServiceBean的BeanDefinition,最后通过BeanDefinitionRegistry将ServieceBean加入到Spring的IoC容器中。
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
//获取对应的Class(实现类的class)
Class<?> beanClass = resolveClass(beanDefinitionHolder);
//获取类上的@Service @DubboService注解
Annotation service = findServiceAnnotation(beanClass);
// The attributes of @Service annotation
//获取到@Service注解 @DubboService注解的属性
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
//获取到服务的接口名称
String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// ServiceBean Bean name
//生成ServiceBean的名称 serviceBean的名称就是ServiceBean:接口全类名 + group + version 这个很简单也不重要 就不分析了 挑重点看
String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
//构建一个ServiceBean
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
//通过BeanDefinitionRegistry将ServieceBean注册
registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}
复制代码
我们看一下buildServiceBeanDefinition()方法,看一下每一个ServiceBean到底是如何构建的。
buildServiceBeanDefinition()
这个方法的作用就是构建ServiceBean的BeanDefinition。这个方法看起来很长,实际上逻辑很简单,就是将接口名,实现类beanName以及@Service,@DubboService注解上的一些信息保存到ServiceBean中。
private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
String serviceInterface,
String refServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
//首先要构建的Bean是一个ServiceBean类型的bean
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));
//set config id, for ConfigManager cache key
//builder.addPropertyValue("id", beanName);
// References "ref" property to annotated-@Service Bean
//设置Bean的ref属性 这个服务实现类的bean的名称
addPropertyReference(builder, "ref", refServiceBeanName);
// Set interface
//设置接口名称
builder.addPropertyValue("interface", serviceInterface);
// Convert parameters into map
//将@Service,@DubboService注解中的parameters属性设置进去
builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
// Add methods parameters
//@Service,@DubboService注解中methods属性设置进去
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
// convert provider to providerIds
//@Service,@DubboService注解中provider属性设置进去
String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
if (StringUtils.hasText(providerConfigId)) {
addPropertyValue(builder, "providerIds", providerConfigId);
}
//@Service,@DubboService注解中registry属性设置进去
// Convert registry[] to registryIds
String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry");
if (registryConfigIds != null && registryConfigIds.length > 0) {
resolveStringArray(registryConfigIds);
builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ','));
}
// Convert protocol[] to protocolIds
//@Service,@DubboService注解中protocol属性设置进去
String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol");
if (protocolConfigIds != null && protocolConfigIds.length > 0) {
resolveStringArray(protocolConfigIds);
builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ','));
}
// TODO Could we ignore these attributes: applicatin/monitor/module ? Use global config
// monitor reference
//@Service,@DubboService注解中monitor属性设置进去
String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
if (StringUtils.hasText(monitorConfigId)) {
addPropertyReference(builder, "monitor", monitorConfigId);
}
// application reference
//@Service,@DubboService注解中application属性设置进去
String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
if (StringUtils.hasText(applicationConfigId)) {
addPropertyReference(builder, "application", applicationConfigId);
}
// module reference
//@Service,@DubboService注解中module属性设置进去
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}
return builder.getBeanDefinition();
}
复制代码
我们看下ServiceBean的结构
可以看到ServiceBean实现了InitializingBean接口,那么ServiceBean在初始化之后一定后执行afterPropertiesSet()方法。
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(getInterface())) {
setPath(getInterface());
}
}
//register service bean and set bootstrap
DubboBootstrap.getInstance().service(this);
}
复制代码
主要就是调用DubboBootstrap.getInstance()获取DubboBootstrap,并且调用service()方法将ServiceBean保存起来。
DubboBootstrap是一个DDL模式的单例。
public static DubboBootstrap getInstance() {
if (instance == null) {
synchronized (DubboBootstrap.class) {
if (instance == null) {
instance = new DubboBootstrap();
}
}
}
return instance;
}
复制代码
service()方法的作用就是将ServiceBean放入到configManager的configsCache中,逻辑很简单,这里就不仔细分析了。
现在已经构建好了ServceBean了,那么如何将ServiceBean发布呢?由于篇幅有限,这部分代码我们在下篇文章继续分析。
总结
本篇文章主要分析了Dubbo是如何扫描@Service,@DubboService注解,如何构建ServiceBean并加入到IoC容器中的。如果有什么疑问,欢迎在下方留言。最后,原创不易,如果本文对你有所帮助,那么点个赞再走吧。