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. Java基础学习(二)—数组

    一.数组的概念 定义: 数组是存储同一种数据类型的多个元素的集合. 数组既可以存储基本数据类型,也可以存储引用数据类型. 格式: 格式1: 数据类型[] 数组名; 格式2: 数据类型 数组名[]; 这 ...

  2. 【模板】链式前向星+spfa

    洛谷传送门--分糖果 博客--链式前向星 团队中一道题,数据很大,只能用链式前向星存储,spfa求单源最短路. 可做模板. #include <cstdio> #include <q ...

  3. python os模块常用方法

    OS模块是Python标准库中的一个操作模块,主要用于处理Linux操作系统中的文件和目录 1.要使用OS必须先导入OS import os 2.os.getcwd()获取当前路径. print os ...

  4. SpringMVC文件上传下载

    不多说,代码: Spring-config.xml<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件, 如果扫描到有Spring的相关注解的类,则把这些类注 ...

  5. VS2003"无法启动调试 没有正确安装调试器"的解决方法

    在用VS2003做项目的时候,经常调试程序,但是有时候回出现如下问题“无法启动调试,没有正确安装调试器,请运行安装程序或修复调试器”.第一次碰到还以为是运气不好,就重新用vs2003安装程序重新修复了 ...

  6. three.js提供的几何体

    1.简单几何体 three.js提供的稍微简单点的几何体包括有:PlaneGeometry(平面).CircleGeometry(圆形).ShapeGeometry(塑性).CubeGeometry( ...

  7. openMP编程(下篇)之数据私有与任务调度

    title: openMP编程(下篇)之数据处理子句与任务调度 tags: ["openMP"] notebook: 分布式程序_Linux --- openMP并行编程中数据的共 ...

  8. OnsenUI 前端框架(三)

    上一章咱们学习了OnsenUI的工具栏.侧边栏和标签栏.通过对页面上这三部分的学习,咱们对混合应用的一个页面有了大体上的认识.从这一章开始,咱们学习OnsenUI混合项目开发过程中会用到的各种各样的组 ...

  9. 读书笔记 effective c++ Item 53 关注编译器发出的警告

    许多程序员常常忽略编译器发出的警告.毕竟,如果问题很严重,它才将会变成一个error,不是么?相对来说,这个想法可能在其它语言是无害的,但是在C++中,我敢打赌编译器的实现者对于对接下来会发生什么比你 ...

  10. 如何使用HTML5自定义数据属性

    在本文中,我将向你介绍如何使用HTML5自定义数据属性.我还将向你介绍一些开发人员在工作中经常使用的优秀实例. 为什么需要自定义数据属性? 很多时候我们需要存储一些与不同DOM元素相关联的信息.这些信 ...