dubbo源码分析三:如何无缝对接spring

dubbo源码分析三:如何无缝对接spring,第1张

dubbo源码分析三:如何无缝对接spring 概述

之前我们分析过dubbo执行rpc调用的时候如何同步和异步之间来回转换的,我们实际使用中发现dubbo直接在service接口上增加@DubboReference注解或者在xml配置文件中配置reference就可以直接在spring中引用到,就可以执行到InvokerInvocationHandler进而执行rpc。

使用起来非常的方便,那这又是怎么做到的呢?咱们今天就一起来看下。

猜想

咱们先猜想一下,在通过debug逐步分析我们的猜想是否正确。

我们在spring管理的类中可以直接注入一个接口,我们可以通过注解的方式在类上加入@Service @Component 或者在xml中通过bean标签的方式等等,但实际我们都没有做,只是在service注入的时候加了@DubboReference。

猜想是dubbo给我们生成了默认的实现类,并且将这个类注入到了spring ioc中,只有这样才可以在任意spring管理的类中注入一个bean。

那谁来@DubboReference解析注解呢?spring提供了一系列的扩展入口可以通过扩展spring来解析我们自己的注解,比如mybatis中的mapper等,那dubbo是怎么扩展的呢?

答案应该在@EnableDubbo 注解中可以找到,带着我们的猜想来看下究竟是怎么做的。

源码佐证

dubbo模块众多,我们见明知意来到dubbo-config-spring模块,具体结构如下:

熟悉spring的朋友看到这应该可以想通了,我们的猜想是正确的。

我们的项目demo结构:

DemoService是个rpc接口,我们在上面增加了@DubboReference注解,我们通过观察dubbo源码的结构可以大致猜想到是ReferenceAnnotationBeanPostProcessor类来做的事情,这是对spring扩展的一个实现。

咱们首先来到@EnableDubbo注解看下源码,这又一个spring扩展点知识。:

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

这里可以看到是的@EnableDubboConfig @DubboComponentScan整合体,再分别进去到2个注解中:

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

    
    boolean multiple() default true;

}

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

    
    String[] value() default {};

这里有个@import注解这个是spring的扩展,可以动态的注册bean,我们猜想这里可能就是dubbo和spring结合的开始,我们在DubboConfigConfigurationRegistrar中打上断点然后运行项目跟下代码。

来到DubboSpringInitializer类中的initContext方法:部分源码如下:

    private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
                                    ConfigurableListableBeanFactory beanFactory) {
        context.setRegistry(registry);
        context.setBeanFactory(beanFactory);

        // customize context, you can change the bind module model via DubboSpringInitCustomizer SPI
        customize(context);

        // init ApplicationModel
        ApplicationModel applicationModel = context.getApplicationModel();
        if (applicationModel == null) {
            if (findContextForApplication(ApplicationModel.defaultModel()) == null) {
                // first spring context use default application instance
                applicationModel = ApplicationModel.defaultModel();
            } else {
                // create an new application instance for later spring context
                applicationModel = frameworkModel.defaultModel().newApplication();
            }

            // init ModuleModel
            ModuleModel moduleModel = applicationModel.getDefaultModule();
            context.setModuleModel(moduleModel);
        }

        // set module attributes
        if (context.getModuleAttributes().size() > 0) {
            context.getModuleModel().getAttributes().putAll(context.getModuleAttributes());
        }

        // 最最关键的地方之一
        registerContextBeans(beanFactory, context);

        // mark context as bound
        context.markAsBound();

        // 最最关键的地方之一
        DubboBeanUtils.registerCommonBeans(registry);
    }

这个方法大致做了这几件事,初始化dubbo的上下文,将dubbo的一些公共类和上下文注册到spring,这里最主要的就是registerCommonBeans方法这个方法注册一大堆dubbo对spring扩展的类,源码如下:

    static void registerCommonBeans(BeanDefinitionRegistry registry) {

        registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);

        registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);

        // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
            ReferenceAnnotationBeanPostProcessor.class);

        // TODO Whether DubboConfigAliasPostProcessor can be removed ?
        // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
            DubboConfigAliasPostProcessor.class);

        // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
//        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
//            DubboBootstrapApplicationListener.class);

        // register ApplicationListeners
        registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
        registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);

        // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
        registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
            DubboConfigDefaultPropertyValueBeanPostProcessor.class);

        // Dubbo config initializer
        registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);

        // register infra bean if not exists later
        registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
    }

注册的这些类呢又是我们刚刚在dubbo-config-spring项目中看到的那些,这更加证实我们的猜想是对的。

不太明白spring的朋友可能就会有疑惑了,这里简单解释下(这里说的注册不是直接注册bean,而是注册到beanDefinitaion中也就是所谓的bean源信息)spring源码解析不在本次探讨内,默认大家对spring源码已经熟悉了。

这部分直接跳过,来到了ReferenceAnnotationBeanPostProcessor类的postProcessBeanFactory方法这个方法循环处理beanFactory最后来到:

protected void prepareInjection(AnnotatedInjectionmetadata metadata) throws BeansException {
        try {
            //find and register bean definition for @DubboReference/@Reference
            for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
                if (fieldElement.injectedObject != null) {
                    continue;
                }
                Class injectedType = fieldElement.field.getType();
                AnnotationAttributes attributes = fieldElement.attributes;
                //重点在这里 registerReferenceBean 他在这里注册了这个玩意 
                String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);
                //associate fieldElement and reference bean
                fieldElement.injectedObject = referenceBeanName;
                injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);

            }
            for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
                if (methodElement.injectedObject != null) {
                    continue;
                }
                Class injectedType = methodElement.getInjectedType();
                AnnotationAttributes attributes = methodElement.attributes;
                String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method);
                //associate fieldElement and reference bean
                methodElement.injectedObject = referenceBeanName;
                injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName);
            }
        } catch (ClassNotFoundException e) {
            throw new BeanCreationException("prepare reference annotation failed", e);
        }
    }

​​​​​​

我们在这里发现了猫腻了,继续跟进registerReferenceBean方法,这个方法是将ReferenceBean写入到beandefinition中核心代码:

       // Register the reference bean definition to the beanFactory
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(ReferenceBean.class.getName());
        beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);

        // set attribute instead of property values
        beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
        beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
        beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);

        // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
        // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
        GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
        targetDefinition.setBeanClass(interfaceClass);
        String id = getPropertyValue(beanDefinition.getPropertyValues(), ReferenceAttributes.ID);

        beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));

        // signal object type since Spring 5.2
        beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);

        beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
        referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
        logger.info("Register dubbo reference bean: "+referenceBeanName+" = "+referenceKey+" at "+member);
        return referenceBeanName;

​​​​​​

这个类的居然是demoService的描述信息

说明我们到核心位置了,我们看下ReferenceBean这个类发现这个是一个FactoryBean这又是一个spring的扩展点,我们看下这个类的关键代码:

public class ReferenceBean implements FactoryBean,
        ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean {
    @Override
    public T getObject() {
        if (lazyProxy == null) {
            createLazyProxy();
        }
        return (T) lazyProxy;
    }

    @Override
    public Class getObjectType() {
        return getInterfaceClass();
    }

    @Override
    @Parameter(excluded = true)
    public boolean isSingleton() {
        return true;
    }
     private void createLazyProxy() {

        //set proxy interfaces
        //see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker, boolean)
        ProxyFactory proxyFactory = new ProxyFactory();
        //此处是重点中的重点
        proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
        proxyFactory.addInterface(interfaceClass);
        Class[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
        for (Class anInterface : internalInterfaces) {
            proxyFactory.addInterface(anInterface);
        }
        if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
            //add service interface
            try {
                Class serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
                proxyFactory.addInterface(serviceInterface);
            } catch (ClassNotFoundException e) {
                // generic call maybe without service interface class locally
            }
        }

        this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
    }

通过createLazyProxy方法将demoService的实例返回,但是我们实际看到的确实dubbo的Proxy类:

我们发现ioc实例化demoService的时候也是实例化的ReferenceBean类:

这时候就有人疑问了,明明是ReferenceBean类,怎么最后又成dubbo的代理类了呢?其实ReferenceBean类是factorybean,它是个特殊bean,它有个方法getObject会返回实际的bean类。

而我们明明看到是调用的spring的动态代理啊怎么回事呢、关键代码在proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());这行,源码如下:

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
        //如果不为空直接生成targetClass 的代理对象
            Class targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

​​​​​

这也是spring的一个扩展点,targetSource属性可以灵活的扩展,所以最后springaop帮忙我们代理的并非demoService也非ReferenceBean而是dubbo的org.apache.dubbo.common.bytecode.Proxy。

总结

至此我们整个流程算是理清楚了,dubbo对接spring时采用了大量的spring扩展实现(后置处理器、factoryBean、import、Proxy-->tragetSource),关于spring的后置处理器的可以看下之前写的《2019年最后一天,呕心沥血之作,解读spring5大后置处理器》。

最后咱们简单画一下流程图:

我们不光要知其然,还要知其所以然,欢迎大家和我一起阅读源码、欣赏源码、借鉴源码,好了这期就到这了,咱们下期再见!


MYSQL系列经典文章

MYSQl 深入探索系列一 redo log

MYSQl深入探索系列二 undo log

MYSQl深入探索系列三 MVCC机制

MYSQl深入探索系列四 服务端优化

MYSQL深入探索系列之五 buffer_pool

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5717831.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存