之前我们分析过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 ReferenceBeanimplements 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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)