Dubbo3.0基于Spring配置注入实现 - 基于@DubboService扫描
记录自己学习Dubbo的一些过程
Dubbo声明服务通常需要做两件事情
(一) 配置dubbo扫描路径
(二) 服务类添加Dubbo注解
- @org.apache.dubbo.config.annotation.Service
- @com.alibaba.dubbo.config.annotation.Service
- @DubboService
这里主要阐述当我们给定配置后,Dubbo是如何扫描@DubboService注解同时做服务发布前的一些准备工作
ServiceAnnotationPostProcessor
ServiceAnnotationPostProcessor
是扫描DubboService的核心类
ServiceAnnotationPostProcessor
是通过DubboAutoConfiguration
自定装配进行加载的,主要实现的接口有BeanDefinitionRegistryPostProcessor
,BeanFactoryPostProcessor
,InitializingBean
PackageScan加载
ServiceAnnotationPostProcessor
实现了InitializingBean
的afterPropertiesSet
接口
在ServiceAnnotationPostProcessor
被加载时,首先会触发afterPropertiesSet
接口
@Override
public void afterPropertiesSet() throws Exception {
this.resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
}
private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
for (String packageToScan : packagesToScan) {
if (StringUtils.hasText(packageToScan)) {
// 使用环境变量替换packageScan中的变量
String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
resolvedPackagesToScan.add(resolvedPackageToScan);
}
}
return resolvedPackagesToScan;
}
加载入口
ServiceAnnotationPostProcessor
实现了BeanDefinitionRegistryPostProcessor
的 postProcessBeanDefinitionRegistry
接口以及BeanFactoryPostProcessor
的 postProcessBeanFactory
接口来完成服务类的扫描
服务类的扫描也分为两种情况,第一种情况是@DubboService加到对应的服务类上来进行发布,第二种情况是通过Spring的@Bean以及@DubboService共同使用来发布的服务
核心功能主要是两方面
- 扫描所有服务类并注册到Spring容器中
- 扫描所有服务类并加载成对应的ServiceBean并注册到容器中
第一种情况加载入口
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
// 扫描入口
scanServiceBeans(resolvedPackagesToScan, registry);
}
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
scanned = true;
if (CollectionUtils.isEmpty(packagesToScan)) {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
return;
}
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
// 添加根据类型扫描的过滤器,只有加上Dubbo提供的注解的类才会被扫描到
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;
}
// 扫描packagescan下所有类并将加了@Service @DubboService注解类作为beanDefinition注册到Spring容器中
// Registers @Service Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// 扫描packagescan下所有类并将加了@Service @DubboService注解的类都转换成BeanDefinitionHolder,用于后续生成ServiceBean的Definition
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) {
// 处理扫描出来的dubbo服务,目的是生成对应的ServiceBean的BeanDefinition
processScannedBeanDefinition(beanDefinitionHolder);
// 缓存已扫描过并加载过的dubbo服务类
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);
}
}
第二种情况加载入口
服务发布方式如下:
@Bean
@DubboService(group="demo", version="1.2.3")
public DemoService demoService() {
return new DemoServiceImpl();
}
加载方式:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.registry == null) {
// In spring 3.x, may be not call postProcessBeanDefinitionRegistry()
this.registry = (BeanDefinitionRegistry) beanFactory;
}
// 获取所有beanfactory中所有存在的BeanDefinition名称
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
// 获取对应BeanDefinition
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 解析BeanDedefinition获取对应服务发布注解上配置参数
Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
if (annotationAttributes != null) {
// process @DubboService at java-config @bean method
processAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
}
}
if (!scanned) {
// In spring 3.x, may be not call postProcessBeanDefinitionRegistry(), so scan service class here
scanServiceBeans(resolvedPackagesToScan, registry);
}
}
private Map<String, Object> getServiceAnnotationAttributes(BeanDefinition beanDefinition) {
// 判断要构造的bean是不是由@bean注解声明出来
if (beanDefinition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(annotatedBeanDefinition);
if (factoryMethodMetadata != null) {
// 三种服务声明注解都尝试去解析一遍,优先获取@DubboService上配置的参数信息
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
if (factoryMethodMetadata.isAnnotated(annotationType.getName())) {
// Since Spring 5.2
// return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap();
// Compatible with Spring 4.x
Map<String, Object> annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName());
return filterDefaultValues(annotationType, annotationAttributes);
}
}
}
}
return null;
}
这里有一点需要注意:
为什么在postProcessBeanFactory里可以直接从容器内获取所有Bean?难道不担心有些bean还没被扫描到吗?
原因:从上面描述过的来看,我们知道第二种情况下需要使用@bean注解来声明一个bean对象,同时@bean注解需要在被加上@configuration注解的类中去使用。
而Spring处理@configuration注解的核心就是ConfigurationClassPostProcessor
,这个处理器会扫描所有被加了configuration注解的类并提取对应的bean信息。
ConfigurationClassPostProcessor
实现的接口是Spring的BeanDefinitionRegistryPostProcessor
,Spring容器在refresh的时候,BeanDefinitionRegistryPostProcessor
是优先于BeanFactoryPostProcessor
的
这也就是以为着,当ServiceAnnotationPostProcessor
的postProcessBeanFactory
被调用时,所有由@bean注解声明出来的bean都已经被解析成对应的BeanDefinition放在容器中了
ServiceBean BeanDefinition生成
发布服务的核心类就是ServiceBean,所有被扫描出来的Dubbo服务类最终都会生成对应的ServiceBean,然后由ServiceBean进行服务的发布
/**
* Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
*
* @param beanDefinitionHolder beanDefinitionHolder是服务类的BeanDefinition
* @see ServiceBean
* @see BeanDefinition
*/
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {
// 根据beanDefinition中的beanName从classloader中加载出对应bean的class
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 解析Class上的注解,如果即加了@DubboService和@Service注解,则优先获取@DubboService
Annotation service = findServiceAnnotation(beanClass);
// 解析注解并获取注解上配置的所有参数
// The attributes of @Service annotation
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
// 解析获取Class对应的接口,即是服务接口
String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
// 获取服务类的bean名称
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 生成ServiceBean的Bean名称
String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
// 生成对应的BeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
// 将生成的Service BeanDefinition放到Spring容器中
registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}
ServiceBean Bean名称
ServiceBean的名称和服务类的Bean名称是不同的
生成规则是 ServiceBean:{interface.className}:{annotation.group}:{annotation.version}
private String generateServiceBeanName(Map<String, Object> serviceAnnotationAttributes, String serviceInterface) {
ServiceBeanNameBuilder builder = create(serviceInterface, environment)
.group((String) serviceAnnotationAttributes.get("group"))
.version((String) serviceAnnotationAttributes.get("version"));
return builder.build();
}
ServiceBean BeanDefinition
ServiceBean的BeanDefinition存在的property来源是@DubboService里定义的各种参数配置
大部分都直接作为property放到BeanDefinition中,但其中有一部分参数配置会被重新加工然后放入到BeanDefinition中
private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
String serviceInterface,
String refServiceBeanName) {
// 获取ServiceBean的BeanDefinition构建器
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
// @DubboService注解中部分需要被排除的参数配置
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"methods", "interfaceName", "parameters");
// 将可被直接添加的参数直接放入BeanDefinitionBuilder中
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
// 此处位置是定义注入的服务对象,在生成ServiceBean时会根据refServiceBeanName获取Bean并通过setRef()方法注入进去
addPropertyReference(builder, "ref", refServiceBeanName);
// Set interface
// 配置需要暴露的接口全限定名
builder.addPropertyValue("interface", serviceInterface);
// Convert parameters into map
builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
// convert provider to providerIds
String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
if (StringUtils.hasText(providerConfigId)) {
addPropertyValue(builder, "providerIds", providerConfigId);
}
// 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
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
String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
if (StringUtils.hasText(monitorConfigId)) {
addPropertyReference(builder, "monitor", monitorConfigId);
}
// module reference
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}
return builder.getBeanDefinition();
}
添加服务至上下文
从上面得加载过程可以看到,最终会生成ServiceBean的BeanDefinition,而ServiceBean实现了InitializingBean
接口,也就是说,在bean初始化过程中,会调用afterPropertiesSet
方法来完成一些对象的自定义操作,来看下这个方法
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(getInterface())) {
setPath(getInterface());
}
}
// 获取域模型
ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
// 将服务放到上下文中
moduleModel.getConfigManager().addService(this);
// 设置部署器为等待状态
moduleModel.getDeployer().setPending();
}
设置服务的主要目的就是为了后续的服务暴露,将所有的服务都加载完设置到上下文中后,会由DefaultModuleDeployer
统一进行服务的发布
发布的过程可以参考 Dubbo3服务发布
最后
Dubbo基于Spring Boot的一个加载方式可以规则为:
- 根据路径或注解扫描所有添加了 @DubboService 注解的类
- 根据服务接口相关信息生成对应的Service BeanDefinition
- 交由Spring构建对应的Bean
- 添加服务至上下文用于后续的服务发布