Dubbo 30 DubboService 的扫描

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

先看总结更高效!!!🎁🎁🎁

这一篇来看一看 @DubboService 标注的类是如何扫描和使用的 , 扫描逻辑如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码// 之前我们就了解到 , Dubbo 通过 PostProcessor 在初始化中做二次处理 , Dubbo 中主要有以下 PostProcessor

// Dubbo 自动配置类
C- DubboAutoConfiguration

// 使用AbstractConfig#getId()设置Dubbo Config bean别名的后处理器类
C- DubboConfigAliasPostProcessor

// 一个BeanFactoryPostProcessor,用于处理java配置类中的@Service注释类和注释bean
// 它也是dubbbo:annotation上的XML BeanDefinitionParser的基础结构类
C- ServiceAnnotationPostProcessor

// 注册一些不存在的基础结构bean
C- DubboInfraBeanRegisterPostProcessor

二 . DubboServicer 加载

2.1 基础案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码@DubboService
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getServiceContext().getRemoteAddress());
return "Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress();
}

@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}

}

2.2 扫描的入口

Dubbo 的扫描同样是通过 BeanPostProcess 进行处理的 , 主要处理类为 ServiceAnnotationPostProcessor ,
在 SpringBoot 加载的时候 , 通过 refreshinvokeBeanFactoryPostProcessors 逻辑发起处理

1
2
3
4
5
6
7
8
9
10
11
12
13
JAVA复制代码// 发起扫描  C- ServiceAnnotationPostProcessor
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;

// Step 1 : 获得扫描的 Package 路径
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// Step 2 : 扫描 路径下的 Service
scanServiceBeans(resolvedPackagesToScan, registry);
} else {
}
}

补充 : postProcessBeanDefinitionRegistry 方法

postProcessBeanDefinitionRegistry 方法是 BeanDefinitionRegistryPostProcessor 接口的唯一方法 , 该方法可以通过 BeanDefinitionRegistry 自定义的注册 Bean .

2.3 Bean 的扫描 (ServiceAnnotationPostProcessor)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
java复制代码// Step 2-1 : 扫描 Bean
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

// PRO0001
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

// PRO0002
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);

// PRO0003
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}

ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);

for (String packageToScan : packagesToScan) {

// 避免重复扫描 -> PRO0004
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
continue;
}

// 扫描所有的 @Service 注解 (Spring) ?
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)) {
// 此处的 serviceClasses 主要是为了 log 处理 , 打印详细的 Service
// PS : 意味着此处可以通过配置该属性打印详细的 Service 类
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
}

for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// -> 2.4 BeanDefinition 的处理
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
// 添加以及加载的 Bean
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
} else {

}
// 添加扫描的 package
servicePackagesHolder.addScannedPackage(packageToScan);
}
}

PRO0001 补充一 : DubboClassPathBeanDefinitionScanner 的占用

1
2
3
4
5
6
7
java复制代码
核心 : DubboClassPathBeanDefinitionScanner 继承于 ClassPathBeanDefinitionScanner , 在这个基础上添加了属性 :
private final ConcurrentMap<String, Set<BeanDefinition>> beanDefinitionMap = new ConcurrentHashMap<>();

在调用父类 findCandidateComponents 的时候 , 会缓存在该类中

目的 :

PRO0002 补充二 : resolveBeanNameGenerator 的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码String CONFIGURATION_BEAN_NAME_GENERATOR = "org.springframework.context.annotation.internalConfigurationBeanNameGenerator"
private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {

BeanNameGenerator beanNameGenerator = null;

if (registry instanceof SingletonBeanRegistry) {
SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry);
beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
}

if (beanNameGenerator == null) {
beanNameGenerator = new AnnotationBeanNameGenerator();
}
return beanNameGenerator;

}

PRO0003 补充三 : AnnotationTypeFilter 处理了哪些 ?

1
2
3
4
5
6
7
8
9
10
java复制代码private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
// @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
DubboService.class,
// @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
Service.class,
// @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
com.alibaba.dubbo.config.annotation.Service.class
);

这里主要是 DubboService , 下方的 2 个 Service 官方标注已过时

补充四 : ServicePackagesHolder 的作用

1
2
3
4
5
6
7
8
java复制代码public class ServicePackagesHolder {
// 可以看到 , 其中主要是2个set 集合用于保存已经加载的 class 和 package
public static final String BEAN_NAME = "dubboServicePackagesHolder";
private final Set<String> scannedPackages = new HashSet<>();
private final Set<String> scannedClasses = new HashSet<>();


}

补充五 : findServiceBeanDefinitionHolders 处理 Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码
private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
BeanNameGenerator beanNameGenerator) {
// 扫描 Bean , 此处实际上是依赖了 Spring 的体系
Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);

Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());
// 所有的Service 类 , 此处的类型 org.springframework.context.annotation.ScannedGenericBeanDefinition
for (BeanDefinition beanDefinition : beanDefinitions) {

String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
beanDefinitionHolders.add(beanDefinitionHolder);

}

return beanDefinitionHolders;

}

2.4 BeanDefinition 的处理

前面会对 BeanDefinitionHolder 进行Bean 处理 ,之前已经了解到这个对象中有2个set 集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码// BeanDefinition 加载主流程 C- ServiceAnnotationPostProcessor
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {

Class<?> beanClass = resolveClass(beanDefinitionHolder);

// 获取 Bean 的注解及属性
Annotation service = findServiceAnnotation(beanClass);
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

// 返回接口类
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

registerServiceBeanDefinition(beanName, serviceBeanDefinition, interfaceClass);

}

Step 1 : 解析接口名

这里分别会从三个属性中尝试获取 : interfaceName / interfaceClass / Class 上面获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Java复制代码// 从@Service注释属性解析服务接口名称
// 注意:如果是通用服务,服务接口类可能在本地找不到
public static String resolveInterfaceName(Map<String, Object> attributes, Class<?> defaultInterfaceClass) {
Boolean generic = getAttribute(attributes, "generic");
// 1. get from DubboService.interfaceName()
String interfaceClassName = getAttribute(attributes, "interfaceName");
if (StringUtils.hasText(interfaceClassName)) {
if (GenericService.class.getName().equals(interfaceClassName) ||
com.alibaba.dubbo.rpc.service.GenericService.class.getName().equals(interfaceClassName)) {
throw new IllegalStateException...;
}
return interfaceClassName;
}

// 2. get from DubboService.interfaceClass()
Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");
if (interfaceClass == null || void.class.equals(interfaceClass)) { // default or set void.class for purpose.
interfaceClass = null;
} else if (GenericService.class.isAssignableFrom(interfaceClass)) {
throw new IllegalStateException....;
}

// 3. get from annotation element type, ignore GenericService
if (interfaceClass == null && defaultInterfaceClass != null && !GenericService.class.isAssignableFrom(defaultInterfaceClass)) {
// 此处拿到的为 interface org.apache.dubbo.demo.DemoService
Class<?>[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);
if (allInterfaces.length > 0) {
interfaceClass = allInterfaces[0];
}
}

return interfaceClass.getName();
}

Step 2 : 构建 buildServiceBeanDefinition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
JAVA复制代码// C- ServiceAnnotationPostProcessor
private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
Class<?> interfaceClass,
String refServiceBeanName) {

BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

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
addPropertyReference(builder, "ref", refServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", 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);
}

// application reference
String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
if (StringUtils.hasText(applicationConfigId)) {
addPropertyReference(builder, "application", applicationConfigId);
}

// module reference
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}

return builder.getBeanDefinition();

}

// 这一步主要是属性的处理 , 没什么看的

Step 3 : 注册 Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
JAVA复制代码// C- ServiceAnnotationPostProcessor
private void registerServiceBeanDefinition(String serviceBeanName, AbstractBeanDefinition serviceBeanDefinition, Class<?> interfaceClass) {
// check service bean
if (registry.containsBeanDefinition(serviceBeanName)) {
BeanDefinition existingDefinition = registry.getBeanDefinition(serviceBeanName);
if (existingDefinition.equals(serviceBeanDefinition)) {
// exist equipment bean definition
return;
}

// ..... 省略异常抛出
throw new BeanDefinitionStoreException(....)
}

registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition);

}


// [PRO] : generateServiceBeanName 的简述
private String generateServiceBeanName(Map<String, Object> serviceAnnotationAttributes, Class<?> interfaceClass) {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group((String) serviceAnnotationAttributes.get("group"))
.version((String) serviceAnnotationAttributes.get("version"));
return builder.build();
}

// PS : 注意 ,此处创建的名称是 ServiceBean:org.apache.dubbo.demo.DemoService
// 这个很重要 , 后面会使用

三 . BeanDefinition 的使用

上文对 BeanDefinition 进行了扫描 , 这一轮来简单看一下这些扫描构建的对象是怎么进行注册的

上文在 generateServiceBeanName 时生成了一个 ServiceBean:org.apache.dubbo.demo.DemoService 的 Bean , 这个 Bean 是非常重要的 .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码// 在 AbstractApplicationContext # refresh 中 , 
public void refresh() throws BeansException, IllegalStateException {
// Bean 的扫描逻辑
invokeBeanFactoryPostProcessors(beanFactory);

//...... 中间处理

// Bean 的注册逻辑
finishBeanFactoryInitialization(beanFactory);

}

// Step 1 : 实例化所有的 Bean
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

// Step 2 : 获取 Bean
for (String beanName : beanNames) {
//...
getBean(beanName)
}

// Step 3: 使用 -> 下一篇看看

此处可以观察到 , Bean 已经在里面了

image.png

总结

啥也不说了 , 看图

Dubbo-DubboService.jpg
简单来说就是扫描Bean后 ,生成特殊的Bean融入容器体系中 , 后续通过 Spring 来加载

看了这么多框架 , Dubbo 对 Spring 的使用已经达到很深的底层了, 就好像看 Spring 源码一样 .

这才叫用框架呀~

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%