06-dubbo整合spring源码分析

开头

dubbo一般会搭配spring集成使用,spring作为一个优秀的开源框架,提供了很多扩展点供第三方框架集成。
说到底,spring和dubbo集成,就是把dubbo中的诸多对象交由spring管理,比如配置类、protocol等。
在了解集成前,需要对上节dubbo的SPI机制有很清晰的了解,并且需要对spring的源码有一定的了解,比如spring的beanDefiniton、扩展点等,不然代码会看的一脸蒙蔽

精简流程

dubbo和spring集成主要做了下面三件事
1.解析配置文件,将配置文件解析成dubbo的bean,生成满足spring条件的beanDefinition
2.@DubboComponentScan包扫描,准备处理自定义包路径下的@Service和@Reference注解
3.处理dubbo服务端@Service注解
4.处理dubbo消费端@Reference注解
5.服务导出(属于Spring容器启动完成后的监听时间,后面单独章节写)

源码流程

地址:
https://www.processon.com/view/link/60e02b8d637689510d6c4184

dubbo-spring整合 (1).png

解析配置文件

dubbo集成spring的总入口为@EnableDubbo,然后点进去@EnableDubboConfig,看到@Import导入了一个DubboConfigConfigurationRegistrar类,import注解表示加载配置文件的时候会加载该类

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

   

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

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>false</code>
     * @revised 2.5.9
     */
    boolean multiple() default true;

}

这里有一个重要的方法 , registerBeans(registry, DubboConfigConfiguration.Single.class);流程见下图,
DubboConfigConfiguration这个类很重要


image.png
@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),
            @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.class)
    })

EnableDubboConfigBinding这个注解在启动时候会加载DubboConfigBindingRegistrar,这个类实现了ImportBeanDefinitionRegistrar接口,该接口是spring提供的一个扩展点,实现这个接口会调用自定义的registerBeanDefinitions接口,可对beanDefinitions进行自定义 。
registerDubboConfigBeans开始对配置文件进行解析,如// dubbo.protocols.p1.port=20880
最终会将所以配置文件翻译成spring的beanDefinition对象

@DubboComponentScan包扫描

在@EnableDubbo注解中集合了@DubboComponentScan注解,@Import导入了启动配置类DubboComponentScanRegistrar。
流程为
@EnableDubbo->@DubboComponentScan->DubboComponentScanRegistrar->registerServiceAnnotationBeanPostProcessor(注册@Service注解)->registerReferenceAnnotationBeanPostProcessor((注册@Reference注解))

@Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("执行DubboComponentScanRegistrar");
    
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
         //处理service注解
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
       //处理reference注解
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

解析@Service注解

在服务者这一端,如果想把自己的接口提供给消费者远程调用,在接口实现类加入@Service注解接口,
@Service注解,和spring的@Service需区别,dubbo的@Service注解是将对应的bean导出成dubbo服务的。
回过头来,registerServiceAnnotationBeanPostProcessor一共会完成三件事
1.生成spring需要的普通beanDefiniton,如DemoServiceImpl
2.生成一个dubbo的ServiceBean,这个bean很重要,后面的服务导出需要依赖他
3.在serviceBean会注册监听spring容器启动事件,spring启动完成后,会调用serviceBean.onEvent事件进行服务导出

@Service源码解析

1.注册ServiceAnnotationBeanPostProcessor,由于该接口实现BeanDefinitionRegistryPostProcessor,所以spring启动调用ServiceAnnotationBeanPostProcessor.postProcessBeanDefinitionRegistry
2.registerServiceBeans扫描包,进行普通Bean注册,扫描被Service注解标注的类
3.注册ServiceBean,详见ServiceAnnotationBeanPostProcessor.buildServiceBeanDefinition
ServiceBean中有很多重要的属性,比如ref-服务实现类、protocol协议、interfaceName接口名字、applicationEventPublisher spring监听器

 private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
      
        // @Service注解
        Annotation service = findServiceAnnotation(beanClass);

       
        // @Service注解上的信息
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

        

        // 生成一个ServiceBean
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

       

    }

4.serviceBean监听spring启动事件,进行服务导出,服务导出比较复杂,后面单独一节写

 @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
        if (!isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            // 服务导出(服务注册)
            export();
        }
    }

解析@Reference注解

在消费者这一端,如果想要远程调用服务者提供的接口,在调用的接口上加入@Reference即可。
回过头来,registerReferenceAnnotationBeanPostProcessor一共会完成三件事,和@Service有点类似
1.生成referenceBean对象
2.生成服务接口代理对象

@Reference源码解析

1.注册ReferenceAnnotationBeanPostProcessor,继承了spring的BeanPostProcessor
2.回调时机:spring依赖注入的时候,调用AnnotationInjectedBeanPostProcessor.postProcessPropertyValues

 @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

        // 寻找需要注入的属性(被@Reference标注的Field)
        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;
    }

3.寻找注入点,2步骤中findInjectionMetadata.buildAnnotatedMetadata
4.找到注入点后,使用new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata注入
5.注入的时候调用AnnotatedInjectionMetadata.inject
6.调用ReferenceAnnotationBeanPostProcessor.doGetInjectedBean
这里做了两件事:1.生成referencedBean代理对象 2.将referencedBean中的beanName注册到spring容器,如demoServiceImpl
7.生成referencedBean代理对象,这里单独拿出来,这里调用了一个很重要的方法getOrCreateProxy,这里会作为服务引入的入口,也就是消费端如何通过注册中心调用到服务端的,流程很复杂,后面单独再写

private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<?> serviceInterfaceType) {
        if (existsServiceBean(referencedBeanName)) { // If the local @Service Bean exists, build a proxy of ReferenceBean
            return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
                    wrapInvocationHandler(referenceBeanName, referenceBean));
        } else {                                    // ReferenceBean should be initialized and get immediately
            // 很重要
            return referenceBean.get();
        }
    }

ReferenceBean几个重要属性:
1.REF_PROTOCOL协议,可以看到采用的SPI机制
private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

  1. PROXY_FACTORY 代理工厂,后面会要生成很多服务的代理对象、dubbo的代理对象
    private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

总结

看到这里,还只是完成了spring的整合,dubbo的核心源码还没有出现,比如服务是如何注册导出的、如何引入、如何调用的,后续会单独写

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容