# Dubbo源码解析:基于注解配置原理解析

在这里插入图片描述

# 使用注解开发一个Dubbo应用

# 客户端

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
    @Reference
    private DemoService demoService;

    @Override
    public String sayHello(String name) {
        return demoService.sayHello(name);
    }
}

dubbo-consumer.properties

dubbo.application.name=dubbo-demo-annotation-consumer
dubbo.registry.address=zookeeper://myhost:2181
public class Application {
    /**
     * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
     * launch the application
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        DemoService service = context.getBean("demoServiceComponent", DemoServiceComponent.class);
        String hello = service.sayHello("world");
        System.out.println("result :" + hello);
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.consumer.comp")
    @PropertySource("classpath:/spring/dubbo-consumer.properties")
    @ComponentScan(value = {"org.apache.dubbo.demo.consumer.comp"})
    static class ConsumerConfiguration {

    }
}

@Configuration:表明这是一个配置类 @EnableDubbo:最主要的一个注解,后面详细分析 @PropertySourc:指定配置文件地址 @ComponentScan:配置包扫描,将类注入容器

# 服务端

@Service
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.getContext().getRemoteAddress());
        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
    }

}

dubbo-provider.properties

dubbo.application.name=dubbo-demo-annotation-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
public class Application {
    /**
     * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
     * launch the 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 {
        @Bean
        public RegistryConfig registryConfig() {
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://myhost:2181");
            return registryConfig;
        }
    }
}

# @EnableDubbo解密

从前面的例子中我们可以发现,起主要作用的注解是@EnableDubbo,接着我们就详细分析一这个注解的作用

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
    
    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default true;

}

@EnableDubbo又再次引入了2个注解。可以看到@EnableDubbo配置的各种包扫描其实是@DubboComponentScan的属性,给@DubboComponentScan用的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {

    boolean multiple() default true;

}

# @EnableDubboConfig注解的作用

@EnableDubboConfig引入了DubboConfigConfigurationRegistrar

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        boolean multiple = attributes.getBoolean("multiple");

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);

        if (multiple) { // Since 2.6.6 https://github.com/apache/incubator-dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
    }

}

DubboConfigConfigurationRegistrar只是往容器中注入了2个Bean。DubboConfigConfiguration.Single.class和DubboConfigConfiguration.Multiple.class 这2个类的作用差不多,只分析一个

@EnableDubboConfigBindings({
        @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
        @EnableDubboConfigBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
        @EnableDubboConfigBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class)
})
public static class Single {

}

类上有@EnableDubboConfigBindings注解,然后引入了DubboConfigBindingsRegistrar类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {

    EnableDubboConfigBinding[] value();

}

DubboConfigBindingsRegistrar这个类的作用很简单,就是将配置文件中的配置转为对应的对象,并注入容器中。

如将以dubbo.application为前缀的配置转为ApplicationConfig对象,将以dubbo.registry为前缀的配置转为RegistryConfig对象

在这里插入图片描述

# @DubboComponentScan的作用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

}

引入DubboComponentScanRegistrar

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

        registerReferenceAnnotationBeanPostProcessor(registry);

    }
}

DubboComponentScanRegistrar会往容器中注入2个BeanPostProcessor

ServiceAnnotationBeanPostProcessor:将包扫描路径中被@Service标注的类,转为ServiceBean,并注入到容器中 ReferenceAnnotationBeanPostProcessor:将被@Reference标注的属性,赋值为对应的ReferenceBean对象

在这里插入图片描述