This is the second day of my participation in the August Text Challenge.More challenges in August

>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…

A. The preface

First see summary more efficient!! 🎁 🎁 🎁

This article will take a look at how classes with the @dubboService annotation are scanned and used. The scanning logic is as follows:

// Dubbo uses PostProcessor to perform secondary processing during initialization

// Dubbo automatic configuration class
C- DubboAutoConfiguration
    
// A post-processor class that uses AbstractConfig#getId() to alias the Dubbo Config bean
C- DubboConfigAliasPostProcessor
    
// A BeanFactoryPostProcessor for handling @Service annotation classes and annotation beans in Java configuration classes
// It is also the base class for the XML BeanDefinitionParser on Dubbbo: Annotation
C- ServiceAnnotationPostProcessor
    
// Register some non-existent infrastructure beans
C- DubboInfraBeanRegisterPostProcessor


Copy the code

DubboServicer load

2.1 Basic Cases

@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; }}Copy the code

2.2 Entry of scanning

Scanning is also handled BeanPostProcess of Dubbo, main processing class for ServiceAnnotationPostProcessor, when SpringBoot loading, Via refresh invokeBeanFactoryPostProcessors logic processing

/ / a scan - ServiceAnnotationPostProcessor C
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
	this.registry = registry;
	
        // Step 1: Obtain the scanned Package path
	Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

	if(! CollectionUtils.isEmpty(resolvedPackagesToScan)) {// Step 2: Scan for services in the path
            scanServiceBeans(resolvedPackagesToScan, registry);
	} else{}}Copy the code

Supplement: postProcessBeanDefinitionRegistry method

The only way to postProcessBeanDefinitionRegistry method is BeanDefinitionRegistryPostProcessor interface, This method can be customized with the BeanDefinitionRegistry registration Bean.

2.3 the Bean’s scan (ServiceAnnotationPostProcessor)

// Step 2-1: Scan the 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) {

        // Avoid duplicate scans -> PRO0004
        if (servicePackagesHolder.isPackageScanned(packageToScan)) {
            continue;
        }

        // Scan all @service annotations (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)) {// The serviceClasses is used to print the Service details for log processing
            // PS: means that the detailed Service class can be printed by configuring this property here
            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 processing
                processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
                // Add and load beansservicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName()); }}else{}// Add the scanned packageservicePackagesHolder.addScannedPackage(packageToScan); }}Copy the code

PRO0001 complement 1: DubboClassPathBeanDefinitionScanner footprint

Core: DubboClassPathBeanDefinitionScanner inheritance in ClassPathBeanDefinitionScanner, on the basis of the added attributes:private final ConcurrentMap<String, Set<BeanDefinition>> beanDefinitionMap = newConcurrentHashMap<>(); The parent class findCandidateComponents is cached in this class when it is called.Copy the code

PRO0002 Supplement 2: The function of the 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;

}
Copy the code

AnnotationTypeFilter: 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. The annotation. Service
    Service.class,
    // @since 2.7.3 Add the compatibility for Legacy Dubbo's @service, the issue: https://github.com/apache/dubbo/issues/4330com.alibaba.dubbo.config.annotation.Service.class ); This is basically DubboService, down here2The official Service annotation is outdatedCopy the code

Add 4: The role of the ServicePackagesHolder

public class ServicePackagesHolder {
    // As you can see, there are mainly two sets of sets to hold the loaded class and package
    public static final String BEAN_NAME = "dubboServicePackagesHolder";
    private final Set<String> scannedPackages = new HashSet<>();
    private final Set<String> scannedClasses = new HashSet<>();
    
    
}
Copy the code

Add five: findServiceBeanDefinitionHolders processing Service


private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders( ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) {
	// Scan beans, which actually rely on Spring's architecture
    Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);

    Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());
	/ / all of the Service class, the type of the org. Springframework. Context. The annotation. ScannedGenericBeanDefinition
    for (BeanDefinition beanDefinition : beanDefinitions) {

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

    }

    return beanDefinitionHolders;

}
Copy the code

2.4 Processing of BeanDefinition

BeanDefinitionHolder is bean-handled earlier, and we saw that there are two sets of sets in this object

/ / load BeanDefinition main process C - ServiceAnnotationPostProcessor
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) { Class<? > beanClass = resolveClass(beanDefinitionHolder);// Get Bean annotations and attributes
    Annotation service = findServiceAnnotation(beanClass);
    Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
	
    // Return the interface classClass<? > 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);

}

Copy the code

Step 1: Parse the interface name

This is attempted from three attributes: interfaceName/interfaceClass/Class

// Resolve the Service interface name from the @service annotation attribute
// Note: If it is a generic service, the service interface class may not be found locally
public static String resolveInterfaceName(Map
       
         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 newIllegalStateException... ; }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 newIllegalStateException.... ; }// 3. get from annotation element type, ignore GenericService
    if (interfaceClass == null&& defaultInterfaceClass ! =null  && !GenericService.class.isAssignableFrom(defaultInterfaceClass)) {
        / / to get here for interface org. Apache. Dubbo. Demo. DemoServiceClass<? >[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);if (allInterfaces.length > 0) {
            interfaceClass = allInterfaces[0]; }}return interfaceClass.getName();
}
Copy the code

Step 2: build buildServiceBeanDefinition

// C- ServiceAnnotationPostProcessor
private AbstractBeanDefinition buildServiceBeanDefinition(Map
       
         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();

}

// This step is mainly the processing of attributes, nothing to see


Copy the code

Step 3: Register the 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;
		}

		/ /... Omitting exception throw
		throw new BeanDefinitionStoreException(....)
	}

	registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition);

}


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

/ / PS: attention, here is the name of creating ServiceBean: org. Apache. Dubbo. Demo. DemoService
// This is very important and will be used later

Copy the code

3. The use of BeanDefinition

We have scanned the BeanDefinition above. This time we will take a quick look at how objects built by these scans are registered

Above when generateServiceBeanName generates a ServiceBean: org. Apache. The dubbo. Demo. DemoService Bean, the Bean is very important.

/ / in AbstractApplicationContext # refresh,
public void refresh(a) throws BeansException, IllegalStateException {
    // The Bean scan logic
    invokeBeanFactoryPostProcessors(beanFactory);
    
    / /... In the middle of processing
    
    // The Bean's registration logic
    finishBeanFactoryInitialization(beanFactory);

}

// Step 1: instantiate all beans
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

// Step 2: Get the Bean
for (String beanName : beanNames) {
    / /...
    getBean(beanName)
}

// Step 3: Use -> Next Step


Copy the code

As you can see here, the Bean is already inside

conclusion

Don’t say anything. Look at the picture

In simple terms, after scanning beans, special beans are generated to be incorporated into the container architecture, and then loaded via Spring

With so many frameworks, Dubbo’s use of Spring has reached a very low level, just like looking at Spring source code.

Now that’s using a frame