Dubbo的服务暴露与服务消费原理(2)-基于注解配置原理解析

用注解的方式启动Spring容器

ProviderConfiguration是自定义的启动类。

public class AnnotationProviderMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

源码分析

在我的启动类AnnotationProviderMain中调用了AnnotationConfigApplicationContext(Class… annotatedClasses)构造方法,在这个构造方法里面,又调用了AnnotationConfigApplicationContext无参构造方法。无参构造方法中实例化了AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。

public AnnotationConfigApplicationContext(Class... annotatedClasses) {
    this();
    this.register(annotatedClasses);
    this.refresh();
}

public AnnotationConfigApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

public void register(Class... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    this.reader.register(annotatedClasses);
}

调用了AnnotatedBeanDefinitionReader​的registerBean方法

public void register(Class... annotatedClasses) {
    Class[] var2 = annotatedClasses;
    int var3 = annotatedClasses.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        Class<?> annotatedClass = var2[var4];
        this.registerBean(annotatedClass);
    }
}

 public void registerBean(Class<?> annotatedClass) {
    this.registerBean(annotatedClass, (String)null, (Class[])null);
}

public void registerBean(Class<?> annotatedClass, String name, Class... qualifiers) {
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        if (qualifiers != null) {
            Class[] var7 = qualifiers;
            int var8 = qualifiers.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Class<? extends Annotation> qualifier = var7[var9];
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                } else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                } else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

来看看BeanDefinitionReaderUtils的registerBeanDefinition方法。通过(beanName, bean)和(beanName, alias别名)的方式注册bean。

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String alias = var4[var6];
            registry.registerAlias(beanName, alias);
        }
    }
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}

private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    return (Environment)(registry instanceof EnvironmentCapable ? ((EnvironmentCapable)registry).getEnvironment() : new StandardEnvironment());
}

使用注解向容器注册类

@Configuration
@EnableDubbo(scanBasePackages = "gdut.ff.provider")
public class ProviderConfiguration   {

    //提供者配置
    @Bean
    public ProviderConfig providerConfig() {
        return new ProviderConfig();
    }

    //应用信息配置
    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("annotation-provider");
        return applicationConfig;
    }

    //注册中心配置
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("127.0.0.1");
        registryConfig.setPort(2181);
        return registryConfig;
    }

    //协议配置
    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20880);
        return protocolConfig;
    }

}

自定义的ProviderConfiguration使用了两个注解,一个是Configuration,另一个是EnableDubbo。

@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)

@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的<bean>,作用为:注册bean对象

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}
/**
 * 能使Dubbo components像Spring Beans一样,是DubboComponentScan和EnableDubboConfig的结合
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {

    /**
     * 定义扫描注解@Service的包
     * 使用类型安全的scanBasePackageClasses()替代基于字符串的包名称
     * @return 扫描的包
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    /**
     *scanBasePackages()的类型安全替代方案,用于指定要扫描带注释的@Service类的程序包。 指定类别的包将被扫描。
     *return 从包中扫描的类
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};


    /**
     * 它指示AbstractConfig是否绑定到多个Spring Bean。
     * return 返回默认值false
     */
    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default false;

}

@EnableDubbo使用了两个注解@EnableDubboConfig和@DubboComponentScan

ConfugurationClassParser的processImports方法调用了DubboConfigConfigurationSelector.selectImports()方法

@EnableDubboConfig根据multiple返回的布尔值,来决定绑定的bean的属性前缀。若为true,表示多个Bean绑定的属性前缀,如果是false,表示单个Bean绑定的属性前缀。

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

    /**
     * 是否绑定多个Bean
     */
    boolean multiple() default false;

}
Beanmultiple的值 false true
ApplicationConfig dubbo.application dubbo.applications
ModuleConfig dubbo.module dubbo.modules
RegistryConfig dubbo.registry dubbo.registries
ProtocolConfig dubbo.protocol dubbo.protocols
MonitorConfig dubbo.monitor dubbo.monitors
ProviderConfig dubbo.provider dubbo.providers
ConsumerConfig dubbo.consumer dubbo.consumers

@EnableDubboConfig还使用了@Import(DubboConfigConfigurationSelector.class)注解

来看看DubboConfigConfigurationSelector类的

selectImports方法。获取EnableDubboConfig的属性multiple的值,

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

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

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

    if (multiple) {
        return of(DubboConfigConfiguration.Multiple.class.getName());
    } else {
        return of(DubboConfigConfiguration.Single.class.getName());
    }
}

来看看DubboConfigConfiguration的实现

public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @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)
    })
    public static class Single {

    }

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}

再来看看@EnableDubboConfigBindings的实现

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

    /**
     * The value of {@link EnableDubboConfigBindings}
     *
     * @return non-null
     */
    EnableDubboConfigBinding[] value();

}

调用了DubboConfigBindingsRegistrar。

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

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

    //获取EnableDubboConfigBindings注解全部的value值。
    AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

    DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
    registrar.setEnvironment(environment);

    for (AnnotationAttributes element : annotationAttributes) {
        //注册到Spring容器。
        registrar.registerBeanDefinitions(element, registry);

    }
}

@DubboComponentScan调用了DubboComponentScanRegistrar注解,会生成ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

    registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

    registerReferenceAnnotationBeanPostProcessor(registry);

}

参考资料

《深入理解Apache Dubbo与实战》

https://blog.csdn.net/u014199…

原文 

https://segmentfault.com/a/1190000022185861

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Dubbo的服务暴露与服务消费原理(2)-基于注解配置原理解析

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址