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. 天气渐热,来片雪花降降温——Android自定义SurfaceView实现雪花效果

    实现雪花的效果其实也可以通过自定义View的方式来实现的(SurfaceView也是继承自View的),而且操作上也相对简单一些,当然也有一些不足啦... 相对于View,SurfaceView有如下 ...

  2. 常见的Java面试题整理

    面试是我们每个人都要经历的事情,大部分人且不止一次,这里给大家总结常见的面试题,让大家在找工作时候能够事半功倍. 1 Switch能否用string做参数? a.在 Java 7 之前, switch ...

  3. 银盛支付ecshop,shopex,shopnc在线支付接口,php版本支付接口开发

    最近应一个客户的要求,给他的一个ecshop商城开发银盛支付在线支付接口.银盛支付服务股份有限公司(简称银盛支付)成立于2009年7月,总注册资本14000万元人民币,员工队伍持续壮大.2011年5月 ...

  4. ArrayList,LinkedList的对比

    ArrayList,LinkedList都是Collection接口的通用实现方式,两者采用了不用的存储策略,用来适应不同场合的需要. 实现方式 ArrayList的内部采用集合的方式存储数据 唯一需 ...

  5. python os模块常用方法

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

  6. Apache日志分割

    1.cronolog安装 采用 cronolog 工具进行 apache 日志分割 http://download.chinaunix.net/download.php?id=3457&Res ...

  7. Linux - 进程间通信 - 命名管道

    1.命名管道的特点: (1)是管道,可用于非血缘关系的进程间的通信 (2)使用命名管道时,梁金成需要用路径表示通道. (3)命名管道以FIFO的文件形式存储于文件系统中.(FIFO:总是按照先进先出的 ...

  8. nginx负载均衡(一)

    背景: 最近公司分配一个项目,做一个直播APP的后台,像这种随时都有用户在线的情况,后台一定不能down掉,而且只做一台服务器的话压力肯定很大,所以考虑用nginx做负载均衡 环境: 三台linux服 ...

  9. C#Execl

    using System.IO; using System.Text; namespace iLIS.Common { /// <summary> /// 生成Excel文档内容 /// ...

  10. 修改linux系统时间和同步

    date 查看当前时间 date -s 15:14:13 修改时间 cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 修改时区 yes cront ...