今天看了一些博文,都是关于dubbo源码解析方面的。以为有必要记一下。java
问题1:spring 如何注入dubbo 的?或者说怎么集成dubbo 的,或者说 dubbo启动时怎么启动spring的?spring
一、首先想要实现 在spring 中 发挥某框架的功能,就必须将该框架注入到springBean 中。
二、dubbo 中 dubbo-container-spring 模块,类 spirngContainer 里面的start方法实现了这功能。
三、上述的start方法 由dubbo 的Main方法调用。。。
四、start方法执行时 会调用spring配置文件路径。若是没有配置Spring xml文件的路径,会默认加载classpath/META-INF下的spring/*.xml文件。express
public void start() { String configPath = ConfigUtils.getProperty(SPRING_CONFIG); if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); context.start(); }
问题2:spring 是如何识别dubbo 标签的?apache
一、首先要清楚,spring 原生的标签 好比一些注解,aop的。bean 的。为何都能认识?
是由于在命名空间中指定了对应的标签好比aop的 xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop"指定这两个,就可使用aop的相关标签。缓存
二、dubbo 也是同样(须要在spring配置文件中添加dubbo的命名空间)dubbo-config-spring包下的META-INF目录下spring.handlers和spring.schemas文件两个文件就是来处理命名空间和xsd。
spring 在识别命名空间时,使用的是NamespaceHandlerSupport抽象类和NamespaceHandler接口
而dubbo 的DubboNamespaceHandler 继承了NamespaceHandlerSupport,会在初始化时,加载并识别dubbo标签,启动dubbo的Main 方法时就加载了上述两个文件进而让spring知道自定义了NamespaceHandlerSupport;app
DubboNamespaceHandler源码框架
/* * Copyright 1999-2011 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.dubbo.config.spring.schema; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import com.alibaba.dubbo.common.Version; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.ModuleConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.AnnotationBean; import com.alibaba.dubbo.config.spring.ReferenceBean; import com.alibaba.dubbo.config.spring.ServiceBean; /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
进入registerBeanDefinitionParser的方法,能够看到他是NamespaceHandlerSupport 的方法。less
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }
第一个参数 是 元素名称,就是告诉spring 我要解析注入这个标签中的class 第二个参数DubboBeanDefinitionParser 实现了BeanDefinitionParser接口,而BeanDefinitionParser接口 是spring 将xml标签解析成BeanDefinition对象的接口,因此DubboBeanDefinitionParser 就是将spring中dubbo 的标签转换成BeanDefinition对象,其元素名称之后还要给Invoker 对象调用服务端方法使用;jvm
*若是是dubbo:protocol标签,dubboh还会检查全部已经包含protocol属性的BeanDefinition且protocol属性对应的值是ProtocolConfig对象的bean,将其属性的protocol值设置成当前的bean引用:
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));ide
*若是是dubbo服务提供者的dubbo:service标签,则还会设置ref属性为对应接口class的实现类bean:
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
问题3:怎么注入的呢?
一、其实就是上面的DubboBeanDefinitionParser类,进行操做的,该类将dubbo:reference这种标签解析成spring可以管理的BeanDefinition对象(我喜欢叫容器,这货是一个map)
二、仔细看dubbo消费者 注入类型是ReferenceBean。其父类是ReferenceConfig 他的属性:
private static finalProxyFactoryproxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); //接口类型 private String interfaceName; private Class<?> interfaceClass; //接口代理类引用 private transient volatileTref; private transient volatileInvoker<?>invoker;
可知,dubbo 将标签转化为对象的时候使用的是动态代理,这点与spring 的IOC大不同,,IOC是反射原理。
ReferenceConfig实现了FactoryBean接口 调用get方法返回代理对象,其子类ReferenceBean 再调用getObject(就是FactoryBean的方法),获取代理对象。
那么 dubbo使用的 是jdk 仍是cgLib呢?
cgLib没有在使用的行列,不知道缘由。但,jdk 和Javassist 两种却被使用,默认使用Javassist动态代理模式(听说效率很高)。可自定义使用jdk代理模式(因这种动态代理仅限接口,使用范围受限,且效率不高,不多用)。
以上是小弟 从网上看到并实际操做实践+本身一些理解的(已经看了很多,但不敢写。怕理解有误)。先记下来。有不稳当的地方后期完善。
2018-11-22补充:
一、在注入和生成动态代理的时候使用Javassist 其实生成的是一个字节码文件,保存在jvm缓存中,等待调用。
简要说明。对于提供者。就是spring 将dubbo标签经过ServiceConfig 解析成了ServiceBean,并生成好了一个invoker,该invoker放在exporter对象下,
exporter对象又放在exporters对象下,而后建立的nettyServer 放在了protocol(单例的)对象下的一个容器里,这样serviceBean 里面就有了可用的invoker(接收consumer 请求,执行本身的invoke方法,以后反射出业务类impl,执行业务代码,将结果经过netty通道返回),nettyServer,等待被调用。
注意:服务提供者在建立出invoker 后 执行业务类的时候使用的是反射技术。不是代理。消费者在执行时,使用代理对象执行invoke方法 才能实现远程调用。