这篇博文是我决心深度学习Dubbo框架时记录的笔记, 主题是Dubbo的拓展点, 下面的几个部分相对来讲比较零散, 貌似是不和主题挂钩的 , 而且是一些很冷门的知识点 , 可是它们确实是深刻学习Dubbo的前置知识java
细化一下上图的各个组成部分:apache
Dubbo本身有个封装类叫URL以下: URL: 全称 Uniform Resources Loactor 统一资源定位符, 它是不可变的, 也是线程安全的编程
其实, Dubbo它做为一款RPC通讯框架, 主体功能就是负责在服务集群中各个点之间进行数据的传递, 打个例子好比: 服务消费者调用服务的提供者,这个过程当中的通讯是Dubbo框架实现的, 通讯的格式就比如自定义协议同样, Dubbo将服务提供者和服务消费者两种之间进行数据传递 须要的协议信息/ 端口号信息/ 请求那个接口 / 参数信息 / 帐号 / 密码信息. 等一系列的信息进行封装,因而上图中的 URL 诞生了api
对URL最直观的理解: URL是dobbo 对一系列数据的封装, 方便代码的编写, 参数的传递缓存
不少人也将URL称为Dubbo的消息总线, 说URL贯穿于Dubbo的上下文, 我感受到这个结论也许是这样得出的, 就是说 Dubbo做为一款RPC框架, 首要的任务就是 RPC 远程过程调用, 怎么样找到提供服务的机器呢? 不管是发起socket 仍是借助Thrift或者Netty这种框架实现也罢, 前提是得知道提供服务的机器在哪里, 它的哪些接口对外暴露服务 , 没错! 这些信息都被Dubbo封装在了URL中安全
# 描述 Dubbo 协议的服务 Dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 # 描述 zookeeper 注册中心 zookeeper://127.0.0.1:2181/org.apache.Dubbo.registry.RegistryService?application=demo-consumer&Dubbo=2.0.2&interface=org.apache.Dubbo.registry.RegistryService&pid=1214&qos.port=33333×tamp=1545721981946 # 描述消费者 服务 consumer://30.5.120.217/org.apache.Dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&Dubbo=2.0.2&interface=org.apache.Dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer×tamp=1545721827784 # for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null 192.168.1.3:20880 # for this case, url protocol = file, url host = null, url path = home/user1/router.js file:///home/user1/router.js?type=script ... 更多参照URL源码
invoker 直译调用者网络
指代程序中的调用对象, 包含了 接口名 / 方法名 / 参数类型列表 / 参数值列表 等架构
怎么理解SPI机制呢?app
若是说SPI是java提供的一种拓展机制, 实际上是不明确的, 结合java自己的语言特性来讲, SPI直观的看就是 基于接口的编程 + 策略模式 + 配置文件 组合实现的动态加载机制, 用大白话解释就是说, 一个框架的设计为了后期的拓展性, 确定先会在顶层设计接口, 而后再为这些接口提供一些默认的实现类, 未了良好的拓展性, 若是想让, 若是想实现容许当前框架 识别 / 加载 / 使用 第三方提供的jar包时 , 就可使用SPI实现接口的动态加载, 只要遵循SPI的规范, java就能将咱们本身的类也加载进JVM供咱们使用负载均衡
提及来总归是模糊的, 看下面的小Demo天然就懂了
// 接口 public interface Person { String getName(); } // 实现类一: public class Student implements Person { @Override public String getName() { return "Student"; } } // 实现类二: public class Teacher implements Person { @Override public String getName() { return "Teacher"; } }
在resource/META-INF/services/ 目录下面添加配置文件, 文件名称为 Person接口的全限定名, 内容以下
com.changwu.javaspi.api.Student com.changwu.javaspi.api.Teacher
测试程序:
public class Test { public static void main(String[] args) { // 加载接口中的实现类 ServiceLoader<Person> load = ServiceLoader.load(Person.class); Iterator<Person> iterator = load.iterator(); while (iterator.hasNext()){ Person next = iterator.next(); System.out.println(next.getName()); } } }
测试结果控制台输出以下:
Student Teacher
Dubbo本身也封装了一套SPI机制, 并将此做为它的扩展点,若是咱们有更好的想法, 可使用Dubbo这个特性加将咱们本身的类注入给Dubbo, 它用法和JDK原生的SPI类似, 不一样点在哪里呢? Dubbo的更强大, 好比相对于JDK的SPI , 它支持根据名称获取出指定的拓展类
一个小demo
@SPI public interface PersonInterface { String getName(); }
public class Student implements PersonInterface { @Override public String getName() { return "Student"; } } public class Teacher implements PersonInterface { @Override public String getName() { return "Teacher"; } }
public class Test { public static void main(String[] args) { // todo 第一点: Dubbo 的SPI算做是他的一个可扩展的机制 ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); PersonInterface carInterface = extensionLoader.getExtension("student"); System.out.println(carInterface.getName()); } }
Spring 的IOC确定是鼎鼎大名的, 很直接的能想到Spring的 @Autowired 注解, 或者的配置文件版本的 <bean>
标签中能够帮咱们自动维护bean之间的相互的依赖的关系
Dubbo 也实现了本身的IOC
好比下面的代码这样: Human.java 中依赖了 PersonInterface 类型的对象, 打眼看上去, 这个对象确定是借助咱们提供的setter方法完成的注入
public class Human implements PersonInterface { private PersonInterface carInterface; //todo 第一个关注点: 咱们的关注点就是说, BeazCar 会帮咱们将哪个实现类当成入参注入进来呢? //todo 答案是 URL ,Dubbo本身封装的URL, 统一资源定位符, Dubbo 会解析入参位置的 url中封装的map //todo map中的key 与 CarInteface中的使用 @Adaptive("car") 注解标记的value对应, 那么值就是将要注入的实际类型 //todo 第二个关注点: Dubbo底层极可能是经过反射使用构造方法完成的属性注入 public void setCarInterface(PersonInterface carInterface) { this.carInterface = carInterface; } @Override public String getColor(URL url) { System.out.println("i am Human "); return "i am Human + " + carInterface.getColor(url); } }
那么问题来了, 假如咱们在配置文件中添加了多个PersonInterface
接口的实现类, 那Dubbo是如何得知须要注入哪个的呢? 答案就在入参位置的URL中, 也就是我在 知识储备二中提到的概念URL
能够看下面这段测试代码, 怎么读下面的这段代码呢?
单独看 (PersonInterface) extensionLoader.getExtension("human");
其实就是前面所说的 Dubbo的SPI机制, 可是在这个基础上多出来的逻辑是啥呢? 是咱们构建了一个URL, 那为何加进去一个URL? 由于上面的示例代码说了, human依赖了一个 PersonInterface 类型的变量, Dubbo就是根据这个URL变量, 进而得知本身到底该该注入哪个变量Personinterface实例的 (由于我提供了两个 一个是Student , 另外一个是Teacher)
此外, 他须要的是map , 咱们给它的也是一个hashmap , 特性就是HashMap的key是不重复的, 用大白话说, 它的底层确定是 key=value 惟一绑定, 而且key也不会出现重复的状况
public class Test { public static void main(String[] args) { // todo 源码的入口, 进入 getExtensionLoader() ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); HashMap<String, String> map = new HashMap<>(); map.put("human", "student"); URL url = new URL("", "",1,map); // todo 继续跟进这个方法 PersonInterface carInterface = (PersonInterface) extensionLoader.getExtension("human"); System.out.println(carInterface.getName(url)); } }
那说了这么多, 到底注入的是哪个对象呢? 从map.put("human", "student");
也能很清楚的看出来, 不就是Student吗? 是的, 确实是它, 可是还少了点东西, 就是Personinterface怎么编写呢? 以下:
// @SPI("stu") 能够给注解添加参数, 参数表示 CarInterface 的默认实现类 @SPI public interface PersonInterface { // todo 下面的注解很重要, 啥意思呢? 能够点进这个注解, 我有一些翻译 // 验证AOP, 依然注入的信息从 url中获取出来 @Adaptive("human") String getName(URL url); }
看上面的代码, 除了@SPI注解, 还有一个注解就是@Adaptive注解, 这个注解的value部分决定了Dubbo到底须要注入哪个 ExtensionObject
由于Dubbo在启动的过程当中会去读取/META-INF/services/ Dubbo-SPI配置文件, 并将每行数据读取维护在一个map中, key就是咱们自定义的名字, 值就是左边的全类名
看下面咱们传递进去的是 human , 表示告诉Dubbo, 让Dubbo拿着human去查找, 很显然Dubbo把咱们前面传递给它的student
找出来, 有了Student 进一步再从上下文中全部的 ExtensionObject中(包含了咱们在配置文件中添加进去的Personinterface的两个实现) 找到具体的注入对象
仍是说, AOP是面向切面编程的思想, Spring本身实现了一套, Dubbo 也实现了一套
验证Dubbo的AOP实现类以下:
public class PersonWrapper implements PersonInterface { // todo 验证Dubbo的自动注入 private PersonInterface carInterface; // todo 根据构造方法进行注入 public PersonWrapper(PersonInterface in){ // 假设传递进来的就是具体的实现类 this.carInterface=in; } // todo 当咱们将 CarWrapper 配置进 Dubbo的 spi中时, 经过Dubbo的Spi获取CarInterface执行时,下面的方法就会被执行 @Override public String getName() { System.out.println("before... "); String color = carInterface.getName(); System.out.println("after... "); return "getName"; } }
public static void main(String[] args) { // todo 第一点: Dubbo 的SPI算做是他的一个可扩展的机制 ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class); PersonInterface carInterface = extensionLoader.getExtension("student"); System.out.println(carInterface.getName()); }
结果以下:
before... after... getName
如过存在多个AOP加强类, 好比从上到下出现的顺序是 w1 w2 ... 那么加强的逻辑添加顺序是 before2 before1
下一篇博文就是探究Dubbo的这些拓展点的底层实现细节了 , 仍是挺带劲的...
最后打一个小广告: 我是bloger 赐我白日梦, 本科大三在读, 热衷java研发, 指望有一份Java相关实习岗位的工做, 能够全职实习半年左右, 最理想城市是北京, 求大佬的内推哇