Spring与Dubbo整合源码分析-P2
前言
上篇文章中Spring与Dubbo整合源码分析-P1中介绍了EnbaleDubbo
注解中的EnableDubboConfig
注解,这个注解主要作用是在Spring
容器初始化时将Dubbo
服务相关的配置bean
的beanDefinition
注入到Spring
容器中,将这些bean
交由Spring
进行管理,以便在合适的时机获取特定的bean
实例。。这篇我们来详细了解一下DubboComponent
注解的作用。
问题
DubboComponentScan
注解的作用是啥?
DubboComponentScan注解解析
点开DubboComponentScan
注解,如下所示:
DubboComponentScan
注解中使用了@import
注解引入了DubboComponentScanRegistrar
。DubboComponentScanRegistrar
实现了ImportBeanDefinitionRegistrar
接口,可以向Spring
容器中的beanDefinitionMap
注册beanDefinition
。
DubboComponentScanRegistrar
DubboComponentScanRegistrar
类的主要方法和注释如下:
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取扫描路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 将扫描路径下具备特定注解的类的beanDefinition注册到Sprin容器中
registerServiceClassPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans
// 注册基础组件
registerCommonBeans(registry);
}
/**
* Registers {@link ServiceClassPostProcessor}
*
* @param packagesToScan packages to scan without resolving placeholders
* @param registry {@link BeanDefinitionRegistry}
* @since 2.5.8
*/
private void registerServiceClassPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// =========================重点===============================
// 生成 ServiceClassPostProcessor 的 beanDefinition
// ServiceClassPostProcessor用来处理特定注解,
// 如 org.apache.dubbo.config.annotation.DubboService
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceClassPostProcessor.class);
// =========================重点===============================
// 设置扫描到包路径
builder.addConstructorArgValue(packagesToScan);
// 设置bean的角色
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 获取beanDefinition
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
// 注册
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
/**
* 获取待扫描路径列表
* @param metadata 注解的元数据信息
* @return java.util.Set<java.lang.String> 待扫描路径列表
*/
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
// 获取 DubboComponentScan注解上的键值对
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
// 获取 key为basePackages的值
String[] basePackages = attributes.getStringArray("basePackages");
// 获取 key为value的值 其实就是上面的basePackages,只不过是别名
String[] value = attributes.getStringArray("value");
// 获取 key为basePackageClasses的值
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
// Appends value array attributes
Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
packagesToScan.addAll(Arrays.asList(basePackages));
// 遍历循环获取每个class所在的包路径
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
// 如果packagesToScan还是为空,则获取启动类的包路径
if (packagesToScan.isEmpty()) {
return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
}
// 返回结果
return packagesToScan;
}
}
DubboComponentScanRegistrar
类的方法都比较简单,只是向Spring
容器中注册特定bean
的beanDefinition
,重点注意的是registerServiceClassPostProcessor
这个方法,向Spring
容器中注册了某些能够处理Dubbo
注解的后置处理器,以便正常启动dubbo服务。
ServiceClassPostProcessor
ServiceClassPostProcessor
实现了BeanDefinitionRegistryPostProcessor
接口,这样就可以对Spring
容器中的某些beanDefinition
进行特殊处理。
ServiceClassPostProcessor
类的主要方法和注释如下:
public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
private static final List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
// 三种不同的注解
DubboService.class,
org.apache.dubbo.config.annotation.Service.class,
com.alibaba.dubbo.config.annotation.Service.class
);
protected final Set<String> packagesToScan;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 注册基础组件 DubboBootstrapApplicationListener
// 这是个监听器,当spring容器发布ContextRefreshedEvent事件时,Dubbo就会触发监听逻辑
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
// 获取待扫描路径set集合
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 扫描包
// 注册 被 @org.apache.dubbo.config.annotation.Service、@DubboService 或者 @com.alibaba.dubbo.config.annotation.Service 标记的bean
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
/**
* 注册 被 @Service、@DubboService 或者 @com.alibaba.dubbo.config.annotation.Service 标记的 bean
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 构建 DubboClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// 获取 beanNameGenerator
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
// 设置 beanNameGenerator
scanner.setBeanNameGenerator(beanNameGenerator);
// 添加过滤器
serviceAnnotationTypes.forEach(annotationType -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
// 遍历循环
for (String packageToScan : packagesToScan) {
// 扫描和实例化扫描路径下所有bean
// 需要注意的是,Sprin中的scanner.scan不仅仅是扫描,还会去实例化被扫描到的bean
scanner.scan(packageToScan);
// 获取被 @org.apache.dubbo.config.annotation.Service、@DubboService 或者 @com.alibaba.dubbo.config.annotation.Service标注的beanDefinitionHolder集合
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
// 遍历循环
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 注册ServiceBean
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @DubboService Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @DubboService was found under package["
+ packageToScan + "]");
}
}
}
}
/**
* Registers {@link ServiceBean} from new annotated {@link DubboService} {@link BeanDefinition}
*
* @param beanDefinitionHolder
* @param registry
* @param scanner
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 服务实现类
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 判断类上是否有DubboService Service 注解
Annotation service = findServiceAnnotation(beanClass);
// 获取注解元数据信息
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 服务实现类实现的接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// 服务实现类对应的bean的名字
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 构建 serviceBeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean 的名称
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
// 如果ServiceBean的beadDefinition没有被注册过
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
// 注册ServiceBean的beadDefinition
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
// ServiceBean的beadDefinition设置键值对
if (!serviceBeanDefinition.getPropertyValues().contains("id")) {
serviceBeanDefinition.getPropertyValues().addPropertyValue("id", beanName);
}
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
}
/**
* 获取类上的@Service @DubboService
*
* @param beanClass the {@link Class class} of Bean
* @return <code>null</code> if not found
* @since 2.7.3
*/
private Annotation findServiceAnnotation(Class<?> beanClass) {
return serviceAnnotationTypes
.stream()
.map(annotationType -> findMergedAnnotation(beanClass, annotationType))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
/**
* 构建ServiceBean的BeanDefinition
*
* @param serviceAnnotation
* @param serviceAnnotationAttributes
* @param interfaceClass
* @param annotatedServiceBeanName
* @return
* @since 2.7.3
*/
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
// 构建 ServiceBean 的 beanDefinition
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
// 忽略掉一些属性,因为这些属性不能做简单的属性赋值,需要经过一些操作,
// 比如protocol这个是需要引用的是bean,而不是字符串,这里我们就要通过字符串去找到bean赋值
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
// 设置ServiceBean的属性值
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
// ===========================重点=========================
// 设置ServiceBean中的ref属性的值
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// 设置ServiceBean中的interface属性的值
builder.addPropertyValue("interface", interfaceClass.getName());
// 设置ServiceBean中的parameters属性的值
builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
// ====================================================
// 下面都是ServiceBean的属性赋值 就不一一写了
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
// 设置ServiceBean中的methods属性的值
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
/**
* Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
*/
String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
if (StringUtils.hasText(providerConfigBeanName)) {
addPropertyReference(builder, "provider", providerConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
*/
String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
if (StringUtils.hasText(monitorConfigBeanName)) {
addPropertyReference(builder, "monitor", monitorConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
*/
String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
if (StringUtils.hasText(applicationConfigBeanName)) {
addPropertyReference(builder, "application", applicationConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
*/
String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
if (StringUtils.hasText(moduleConfigBeanName)) {
addPropertyReference(builder, "module", moduleConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
*/
String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
if (!registryRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("registries", registryRuntimeBeanReferences);
}
/**
* Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
*/
String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
if (!protocolRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
return builder.getBeanDefinition();
}
}
ServiceClassPostProcessor
这个后置处理器主要是处理被@DubboService
或者是com.alibaba.dubbo.config.annotation.Service
注解的bean
,利用Spring
容器设置这些bean
的beanDefinition
的某些属性,然后再将bean
实例化出来,留在后面使用。这里特别需要注意的一点是ServiceClassPostProcessor#postProcessBeanDefinitionRegistry这个方法会注册一个监听器,DubboBootstrapApplicationListener,而这个监听器是Dubbo
服务导出的起点。这里先提一下,在后面Dubbo
服务导出的文章会进行解释。
debug过程
DubboComponentScanRegistrar#registerServiceClassPostProcessor
DubboComponentScanRegistrar#registerServiceClassPostProcessor
ServiceClassPostProcessor
postProcessBeanDefinitionRegistry
registerServiceBeans
registerServiceBean
DefaultListableBeanFactory#registerBeanDefinition
回答问题
本章的问题是,DubboComponentScan
注解的作用是啥?
结合本章的内容,DubboComponentScan
注解上使用了@Import
注解引入了DubboComponentScanRegistrar
类。这个类的作用是向Spring
容器中注册Dubbo
服务中某些特定bean
的beanDefinition
,将这些bean
交由Spring
进行管理,以便在合适的时机获取特定的bean
实例。同时在这个类中,registerServiceClassPostProcessor
这个方法还会引入ServiceClassPostProcessor
类,这个类的postProcessBeanDefinitionRegistry
方法会向Spring
容器注册被@DubboService
、@org.apache.dubbo.config.annotation.Service
和@com.alibaba.dubbo.config.annotation.Service
标注的bean
的beanDefinition
,将这些bean
交由Spring
进行管理,以便在合适的时机获取特定的bean
实例。