Dubbo 3.0 : DubboService 的扫描

3,471 阅读5分钟

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

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

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

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

// 之前我们就了解到 , 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 基础案例

@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 逻辑发起处理

// 发起扫描  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)

// 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 的占用


核心 : DubboClassPathBeanDefinitionScanner 继承于 ClassPathBeanDefinitionScanner , 在这个基础上添加了属性 :
private final ConcurrentMap<String, Set<BeanDefinition>> beanDefinitionMap = new ConcurrentHashMap<>();

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

PRO0002 补充二 : resolveBeanNameGenerator 的作用

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 处理了哪些 ?

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 的作用

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


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 集合

// 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 上面获取

// 从@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

// 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

// 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 是非常重要的 .

// 在 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 源码一样 .

这才叫用框架呀~~~