Dubbo微容器详解

ExtensionLoader

ExtensionLoader是Dubbo中的SPI的实现方法,它是Dubbo框架的微容器,也为框架提供各种组件的扩展点

三种注解

  • SPI
  • Adaptive
  • Activate

How to Work

先看Java自带SPI(Service Provider Interface)

  • ServiceLoader是一个简单的服务提供者加载工具

    (A simple service-provider loading facility)

  • since JDK 1.6

  • 简单的例子

  • 一个关于Car的Interface

public interface Car {
void run();
}
  • 2个Car是具体实现
public class RacingCar implements Car {
@Override
public void run() {
System.out.println("RacingCar Running...");
}
} public class SportCar implements Car {
@Override
public void run() {
System.out.println("SportCar Running...");
}
}
  • 调用类
public class Main {
public static void main(String[] agrs){
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
serviceLoader.forEach(car -> {
car.run();
});
}
}
  • META-INF/services/com.youzan.soa.Car 内容

    com.youzan.soa.RacingCar
    com.youzan.soa.SportCar
  • 工程目录结构

Dubbo SPI机制

Dubbo SPI机制Java的SPI机制相似,又比它多了一些功能

  1. 提供注解方式,可以方便的扩展实现
  2. 依赖注入功能

如何实现

  • 构造一个ExtensionLoader实例
ExtensionLoader<SimpleExt> extensionLoader = ExtensionLoader.getExtensionLoader(SimpleExt.class);
  • 流程

  • 结合源码分析

private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
//...省略次要部分代码
}
} Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

真正加载SpiExtensionFactory和AdaptiveExtensionFactory的是loadExtensionClasses方法,干活是loadFile方法。loadExtensionClasses会去查找三个路径下对应的工厂类扩展点

在构造AdaptiveExtensionFactory的ExtensionLoader实例并不需要加载依赖

也就是AdaptiveExtensionFactory的ExtensionLoader实例objectFactory=null,而SimpleExt的ExtensionLoader实例objectFactory是AdaptiveExtensionFactory。

private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
//... 省略部分代码代码
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}

获取扩展点

  • 调用
	SimpleExt simpleExt = extensionLoader.getExtension("impl1");
  • 流程

  • 源码分析

createExtension方法

private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
//...省略
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
//...省略
return instance;
} catch (Throwable t) {
//...省略
}
}

createExtension是创建扩展点的入口,先通过getExtensionClasses加载三个路径下对应的扩展类,然后调用injectExtension注入依赖

详细分析injectExtension方法

private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
//...省略
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}

因为SimpleExt的ExtensionLoader实例objectFactory是AdaptiveExtensionFactory,所以if分支的代码会执行。SimpleExt实例impl1有依赖的属性dao如下, injectExtension是通过set方法注入依赖。 如果此时依赖没有创建好,通过objectFactory.getExtension递归创建扩展点

public class SimpleExtImpl1 implements SimpleExt {
public Dao dao;
public void setDao(Dao dao){
this.dao = dao;
}
public String echo(URL url, String s) {
return "Ext6Impl1-echo-" + ext1.echo(url, s);
}
}
  • objectFactory.getExtension, objectFactory的实现类是AdaptiveExtensionFactory, getExtension方法是一个入口,最终干活的是在factories中即是SpiExtensionFactory
	public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
  • SpiExtensionFactory.getExtension的内部调用ExtensionLoader.getExtensionLoader递归加载依赖
	public class SpiExtensionFactory implements ExtensionFactory {
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (loader.getSupportedExtensions().size() > 0) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}

至此SimpleExt的扩展点及其依赖都已经加载完毕,是不是和spring的依赖注入有点相似,简易版本的Spring依赖管理

  • 下一篇讲介绍Dubbo微容器的启动

Dubbo微容器(Cooma)详解的更多相关文章

  1. 微信小程序flex容器属性详解

    flex容器属性详解 flex-direction决定元素的排列方向 flex-wrap决定元素如何换行 flex-flow 是 flex-direction 和flex-wrap的简写 justif ...

  2. Dubbo 原理和机制详解 (非常全面)

    Dubbo 是一款Java RPC框架,致力于提供高性能的 RPC 远程服务调用方案.作为主流的微服务框架之一,Dubbo 为开发人员带来了非常多的便利. 大家好,我是 mikechen,专注分享「互 ...

  3. 自动化集成:Kubernetes容器引擎详解

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译.打包.镜像构建.部署等操作:本篇文章主要描述Kubernetes引擎用法. 一.基础简介 Kube ...

  4. JavaWeb学习篇之----容器Response详解

    今天在来看一下Response容器的相关知识,其实这篇blog早就应该编写了,只是最近有点忙,所以被中断了.下面我们就来看一下Response容器的相关知识吧.Response和我们即将在后面说到的R ...

  5. JavaWeb学习篇之----容器Request详解

    前篇说到了Response容器对象,这篇我们就来看一下Request容器对象,之前也说过了,这个两个容器对象是相对应的,每次用户请求服务器的时候web容器就会给创建这对容器对象,他们是共存亡的,当然R ...

  6. 161130、Dubbo+SpringMVC工程创建详解

    Dubbo出现的目的是为了应对现在高并发,高数据量请求的问题.目前的垂直应用架构已经无法满足现在大数据的冲击,SOA就应运而生,而Dubbo在国内使用的还是比较多,稳定性也比较不错. 架构 节点角色说 ...

  7. docker容器dockerfile详解

    docker公司在容器技术发展中提出了镜像分层的理念,可以说也是这个革命性的理念让原本只不过是整合linux内核特性的容器,开始野蛮生长. docker通过UnionFS联合文件系统将镜像的分层实现合 ...

  8. C++queue容器学习(详解)

    一.queue模版类的定义在<queue>头文件中. queue与stack模版非常类似,queue模版也需要定义两个模版参数,一个是元素类型,一个是容器类型,元素类型是必要的,容器类型是 ...

  9. spring boot容器启动详解

    目录 一.前言 二.容器启动 三.总结 =======正文分割线====== 一.前言 spring cloud大行其道的当下,如果不了解基本原理那么是很纠结的(看见的都是约定大于配置,但是原理呢?为 ...

随机推荐

  1. Twitter数据抓取的方法(一)

    Scraping Tweets Directly from Twitters Search Page – Part 1 Published January 8, 2015 EDIT – Since I ...

  2. Java 判断回文字符串有多少和其中的最大字符串

    一.简介代码功能 该代码的功能可以实现对任意的一段字符串进行判断是否有回文,回文有哪些,和其中的最大回文. 二.代码部分 1.全局变量 static String hws = "" ...

  3. Azure IoT 技术研究系列3-设备到云、云到设备通信

    上篇博文中我们将模拟设备注册到Azure IoT Hub中:我们得到了设备的唯一标识. Azure IoT 技术研究系列2-设备注册到Azure IoT Hub 本文中我们继续深入研究,设备到云.云到 ...

  4. Extjs6(一)——用sencha cmd建立一个ExtJs小项目

    本文基于ext-6.0.0 一.用sencha cmd建立一个ExtJs小项目 首先,需要一个命令行工具.进入extjs所在目录. 然后,输入:sencha -sdk [ExtJs6.0文件夹地址] ...

  5. JS+html--实现图片轮播

    大家肯定见过某些网站一个炫酷的页面,就是图片轮播,也就是我们常说的幻灯片播放.对于初学者来说,可能会有点头疼,没关系,小李在这给大家献上自己刚刚写好的关于图片轮播的代码. 以下功能的实现用了jQuer ...

  6. Memcached安装使用教程及常见问题

    一.Windows下安装memcahed 1.下载memcache的windows稳定版,解压放某个盘下面,比如在c:/memcached2.在终端(也即cmd命令界面)下输入"c:/mem ...

  7. [转]Installing Memcached on Windows

    Installing Memcached on Windows 原文链接https://commaster.net/content/installing-memcached-windows   Sub ...

  8. bzoj2144 【国家集训队2011】跳跳棋

    Description 跳跳棋是在一条数轴上进行的.棋子只能摆在整点上.每个点不能摆超过一个棋子.我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置.我们要通过最少的跳动把他 ...

  9. apache用户认证,ssl双向认证配置

    安装环境: OS:contos 6.4 httpd:httpd-2.2.15-59.el6.centos.i686.rpm openssl:openssl-1.0.1e-57.el6.i686.rpm ...

  10. WebGIS开源解决方案之开发环境搭建(四)

    续前几篇文章,前面陆续介绍了开源GIS服务器Geoserver,开源数据库Postpresql以及开源前端udig的安装和基本使用. WebGIS前端开发,可以选择arcgis for javascr ...