1. Overall architecture

Note: This article is based on dubo2.7.5, in which service exposure is not a feature of ServiceBean.

1.1 Startup Classes and Configurations

package com.zyz;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

public class Application {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
    @PropertySource("classpath:/spring/dubbo-provider.properties")
    static class ProviderConfiguration {

    }
}


Copy the code

2. Source code analysis

2.1 EnableDubbo annotations

Start with the annotation, which scans the classes under the specified package, the @service and @Reference annotations, and processes them.

This annotation contains two important annotations @enableDubboConfig and @DubboComponentScan. One is to convert properties in the configuration file to beans and assign values, and the other is to scan @service and @Reference annotations.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
@EnableDubboLifecycle
Copy the code

2.1.1 @ EnableDubboConfig annotation

Enter the annotations, found @ Import DubboConfigConfigurationRegistrar class

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
Copy the code

2.1.2 DubboConfigConfigurationRegistrar class

As is customary in Spring, a class annotated with @import performs a registration method to register itself as a Bean. So let’s look at the registration method for this class.

@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName())); boolean multiple = attributes.getBoolean("multiple"); / / a Single binding configuration registerBeans (registry, DubboConfigConfiguration. Single. Class); / / multiple instances binding configuration if (multiple) {/ / Since 2.6.6 https://github.com/apache/dubbo/issues/3193 registerBeans (registry, DubboConfigConfiguration.Multiple.class); } / / registered DubboConfigAliasPostProcessor registerDubboConfigAliasPostProcessor post processor (registry); / / registered NamePropertyDefaultValueDubboConfigBeanCustomizer registerDubboConfigBeanCustomizers (registry); }Copy the code

This is what single binding and multi-instance binding mean:

Single binding:

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
Copy the code

Multi-instance binding:

Dubbo. Separate protocols. P1. Id = dubbo1 dubbo. Separate protocols. (p1) name = dubbo dubbo. Separate protocols. (p1) port = 20881 dubbo. Separate protocols. P1. Host = 0.0.0.0  dubbo.protocols.p2.id=dubbo2 dubbo.protocols.p2.name=dubbo dubbo.protocols.p2.port=20882 Dubbo. Separate protocols. P2. Host = 0.0.0.0 dubbo. Separate protocols. P3. Id = dubbo3 dubbo. Separate protocols. P3. Name = dubbo dubbo. Separate protocols. P3. Port = 20883 Dubbo. Separate protocols. P3. Host = 0.0.0.0Copy the code

2.1.3 DubboConfigConfiguration class

The purpose of this class is to bind the configuration information in the configuration file to a specific class.

Protocol is bound to protocolConfig. class, for example.

public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
    })
    public static class Single {

    }

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}
Copy the code

2.1.4 @ EnableConfigurationBeanBindings

This annotation is contains all configuration information, note that the annotation @ Import the ConfigurationBeanBindingsRegister class.

2.1.5 ConfigurationBeanBindingsRegister class

Since this is introduced by the @import annotation, look at the class registration method.

@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// Get attributes in the configuration file (stored with key-value) AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName())); / / configuration properties, is to dubbo. Registrie these values AnnotationAttributes [] AnnotationAttributes = attributes. GetAnnotationArray (" value "); ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar(); registrar.setEnvironment(environment); AnnotationAttributes Element: annotationAttributes) { registrar.registerConfigurationBeanDefinitions(element, registry); }}Copy the code

2.1.6 registerConfigurationBeanDefinitions () method

This method registers the properties in the configuration file as a bean

protected void registerConfigurationBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) { String prefix = environment.resolvePlaceholders(attributes.getString("prefix")); Class<? > configClass = attributes.getClass("type"); boolean multiple = attributes.getBoolean("multiple"); boolean ignoreUnknownFields = attributes.getBoolean("ignoreUnknownFields"); boolean ignoreInvalidFields = attributes.getBoolean("ignoreInvalidFields"); registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry); }Copy the code

2.1.7 registerConfigurationBeans () method

This method registers the values in the configuration properties as bean-by-bean, but there is no assignment yet. Instead, the assignment is made later by registering an assignment post-processor.

private void registerConfigurationBeans(String prefix, Class<? > configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, BeanDefinitionRegistry registry) { Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix); if (CollectionUtils.isEmpty(configurationProperties)) { if (log.isDebugEnabled()) { log.debug("There is no property for binding to configuration class [" + configClass.getName() + "] within prefix [" + prefix + "]"); } return; } // Set<String> beanNames = multiple? resolveMultipleBeanNames(configurationProperties) : singleton(resolveSingleBeanName(configurationProperties, configClass, registry)); // Register as a bean, but do not assign a value. for (String beanName : beanNames) { registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, configurationProperties, registry); } / / registered post processor binding bean attribute value registerConfigurationBindingBeanPostProcessor (registry); }Copy the code

2.1.8 ConfigurationBeanBindingPostProcessor class

This is where knowledge of the Spring post-processor is used, first initializing the class via postProcessBeanFactory, and then assigning the previously initialized bean when the Spring container calls the first post-processor.

@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // Initialize the bean factory this.beanFactory = beanFactory; / / initialize ConfigurationBeanBinder binder initConfigurationBeanBinder (); initBindConfigurationBeanCustomizers(); }Copy the code
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { BeanDefinition beanDefinition = getNullableBeanDefinition(beanName); If (isConfigurationBean(beanDefinition)) {bindConfigurationBean(Bean, beanDefinition); beanDefinition); // After binding, execute setName and getName methods customize(beanName, bean); } return bean; }Copy the code

Dubbo has now converted the properties we configured in the configuration file into Dubbo’s configuration Bean and assigned the values.

Summarize the overall process.

2.2 Obtaining the @Service Process

View the @ DubboComponentScan annotations, found @ Import DubboComponentScanRegistrar class, then the class is @ the class of Service and @ the Reference.

Look at the registration method for this class.

@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// Get scan package directory, That is, dubo.scan. Base-packages or @enableDubbo (scanBasePackages = "com.zyz.provider. Service ") Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); / / registered ServiceAnnotationBeanPostProcessor post processor, was once the scan to a @ Service annotations and it by its annotation class as a Dubbo Service, For service export registerServiceAnnotationBeanPostProcessor (packagesToScan, registry); / / 2.3 analysis registerReferenceAnnotationBeanPostProcessor (registry); }Copy the code

2.2.1 registerServiceAnnotationBeanPostProcessor () method

This method is mainly the ServiceAnnotationBeanPostProcessor class registration

2.2.2 ServiceAnnotationBeanPostProcessor class

This class is the one registered with the @service annotation.

@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // Registering registerBeans DubboBootstrapApplicationListener class (registry, DubboBootstrapApplicationListener. Class); Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); if (! Collectionutils. isEmpty(resolvedPackagesToScan) {// Register ServiceBean registerServiceBeans(resolvedPackagesToScan, registry); } else { if (logger.isWarnEnabled()) { logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!" ); }}}Copy the code

2.2.3 registerServiceBeans () method

Private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {// Initialize Dubbo's own scan class, Scanning @ Service annotations DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner (registry, environment, resourceLoader); // Initialize BeanNameGenerator BeanNameGenerator = resolveBeanNameGenerator(registry); scanner.setBeanNameGenerator(beanNameGenerator); scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class)); /** * Add the compatibility for legacy Dubbo's @Service * * The issue : https://github.com/apache/dubbo/issues/4330 * @ since 2.7.3 * / scanner. AddIncludeFilter (new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class)); For (String packageToScan: packagesToScan) {// Register the class annotated by @service, in this case register Spring's Bean scanner.scan(packageToScan); / / find the Bean in the Spring (registered) above the Set < BeanDefinitionHolder > beanDefinitionHolders = findServiceBeanDefinitionHolders (scanner, packageToScan, registry, beanNameGenerator); if (! CollectionUtils.isEmpty(beanDefinitionHolders)) { for (BeanDefinitionHolder beanDefinitionHolder : BeanDefinitionHolders) {// What is registered here is Dubbo's ServiceBean registerServiceBean(beanDefinitionHolder, Registry, scanner); } if (logger.isInfoEnabled()) { logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +  beanDefinitionHolders + " } were scanned under package[" + packageToScan + "]"); } } else { if (logger.isWarnEnabled()) { logger.warn("No Spring Bean annotating Dubbo's @Service was found under package[" + packageToScan + "]"); }}}}Copy the code

2.2.4 registerServiceBean () method

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) {/ / get be @ the Class of Service annotation Class <? > beanClass = resolveClass(beanDefinitionHolder); Annotation Service = findServiceAnnotation(beanClass); / / get @ the attribute values of Service annotations AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes (Service, false, false); // Get the interface Class<? > interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass); / / get the class name String annotatedServiceBeanName = beanDefinitionHolder. GetBeanName (); AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName); // Get ServiceBeanName, Such as: ServiceBean: com. Zyz. DemoService: async String beanName = generateServiceBeanName (serviceAnnotationAttributes, interfaceClass); if (scanner.checkCandidate(beanName, ServiceBeanDefinition)) {/ / registered ServiceBean registry. RegisterBeanDefinition (beanName serviceBeanDefinition); 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?" ); }}}Copy the code

At this point, the analysis is complete, this part of the code is relatively simple, is registered two beans.

2.3 Obtaining @Reference Process

In section 2.2 a registerReferenceAnnotationBeanPostProcessor () method, this method is a registered ReferenceAnnotationBeanPostProcessor this post processor, Classes and methods used to get @Reference annotations and inject properties. The class inherited AbstractAnnotationBeanPostProcessor class again.

2.3.1 AbstractAnnotationBeanPostProcessor

Will call the class first postProcessPropertyValues () method

@Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {// Get all injected attributes InjectionMetadata Metadata = findInjectionMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex); } return pvs; }Copy the code

2.3.2 findInjectionMetadata () method

private InjectionMetadata findInjectionMetadata(String beanName, Class<? String cacheKey = (stringutils.hasLength (beanName)? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata ! = null) { metadata.clear(pvs); } try {// Build attribute metadata = buildAnnotatedMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } catch (NoClassDefFoundError err) { throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() + "] for annotation metadata: could not find class that it depends on", err); } } } } return metadata; }Copy the code

2.3.3 buildAnnotatedMetadata () method

private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<? > beanClass) {/ / to get class @ Reference Collection < AbstractAnnotationBeanPostProcessor. AnnotatedFieldElement > fieldElements = findFieldAnnotationMetadata(beanClass); / / get in the way that @ Reference Collection < AbstractAnnotationBeanPostProcessor. AnnotatedMethodElement > methodElements = findAnnotatedMethodMetadata(beanClass); return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements); }Copy the code

2.3.4 inject () method

Return postProcessPropertyValues method, look at this line

metadata.inject(bean, beanName, pvs);
Copy the code

See above know that returns this object AbstractAnnotationBeanPostProcessor. AnnotatedInjectionMetadata, Here is called AnnotatedInjectionMetadata. Inject traversal method, there are these two properties due to the class

        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements;

        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements;
Copy the code

So will traverse the respectively, is called AnnotatedFieldElement. Inject method.

@override protected void inject(Object bean, String beanName, PropertyValues PVS) throws Throwable { > injectedType = field.getType(); InjectedObject = getInjectedObject(Attributes, bean, beanName, injectedType, this); ReflectionUtils.makeAccessible(field); field.set(bean, injectedObject); }Copy the code

2.3.5 getInjectedObject () method

protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<? > injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception { String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement); InjectedObject = injectedObjectsCache. Get (cacheKey); // Cache does not exist, Initialize if (injectedObject == null) {injectedObject = doGetInjectedBean(Attributes, bean, beanName, injectedType, injectedElement); // Customized inject-object if necessary injectedObjectsCache.putIfAbsent(cacheKey, injectedObject); } return injectedObject; }Copy the code

2.3.6 doGetInjectedBean () method

Here referencedBeanName and referenceBeanName are not easy to understand, I hope they can be optimized in the future.

@Override protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<? > injectedType, InjectionMetadata. InjectedElement InjectedElement) throws the Exception {/ / generates ServiceBean: com. Zyz. DemoService: the default String  referencedBeanName = buildReferencedBeanName(attributes, injectedType); //@Reference(version=default) com.zyz.DemoService String referenceBeanName = getReferenceBeanName(attributes, injectedType); / / create ReferenceBean ReferenceBean ReferenceBean = buildReferenceBeanIfAbsent (referenceBeanName, attributes, injectedType); // Register the ReferenceBean(referencedBeanName, ReferenceBean, Attributes, injectedType); / / cache cacheInjectedReferenceBean (referenceBean injectedElement); Return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType); }Copy the code

2.3.7 getOrCreateProxy () method

private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<? > serviceInterfaceType) {if (existsServiceBean(referencedBeanName)) { Return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType}, wrapInvocationHandler(referenceBeanName, referenceBean)); } else {// We should go to the registry to get the service and create the object. }}Copy the code

2.3.8 referenceBean. The get ()

For this method, the follow-up to optimize, and if can ensure that access to the remote service logic, after DubboLifecycleComponentApplicationListener events, can avoid start DubboBootstrap. The init (), Because DubboBootstrap is initiated after DubboLifecycleComponentApplicationListener.

public synchronized T get() { if (destroyed) { throw new IllegalStateException("The invoker of ReferenceConfig(" + url +  ") has already destroyed!" ); } if (ref == null) {// initialize remote service init(); } return ref; }Copy the code

So far the source code analysis is finished.