2019独角兽企业重金招聘Python工程师标准>>>

本文重点讲述SPI机制,从jdk和dubbo

1、jdk spi机制

2、dubbo spi实现

首先spi是什么?

SPI是为某个接口寻找服务实现的机制。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

其次java spi是怎么找到实现类的?

java spi和所有实现接口的厂商有一个俗称的约定,只要将META-INF/services文件夹下生成一个和抽象类全名称(路径+类名称)相同的配置文件,那么厂商的jar包只要在工程路径下就能找到实现类。这种方式主要是解决不同厂商不同实现类的加载问题。在不修改java文件的情况下,如何才能够定位到不同的实现类。

JDK的spi机制有一个缺点,就是如果多个厂商的spi实现的jar包都在路径下,那么就要加载所有的实现类,这样很浪费资源。dubbo中每一种接口都有很多种实现,如果使用jdk这种方式自然做不到优雅的根据一个接口来获得该接口的实现。dubbo的目标就是:根据你配置的name获取某一个特定的接口实现,没有用到的其他接口实现就不能被实例化,免得浪费。因此,dubbo就按照SPI机制的原理自己实现了一套扩展机制。为实现dubbo的扩展机制,dubbo的SPI做到了一下三个方面。

1 可以方便的获取某一个想要的扩展实现,java的SPI机制就没有提供这样的功能
2 对于扩展实现IOC   依赖注入功能

3 对扩展采用装饰器模式进行功能增强,类似AOP实现的功能

dubbo的SPI实现具体如下。

首先定义一个SPI注解类

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface SPI {

/**     * 缺省扩展点名。     */String value() default "";

}

dubbo里面很多的接口都打了SPI注解,这些注解的实现要从一下三个文件夹下去寻找实现。

META-INF/dubbo/internal/   //dubbo内部实现的各种扩展都放在了这个目录了

META-INF/dubbo

/META-INF/services/

文件里面的内容全部都是key-value的形式存在。dubbo的各种接口有很多类型的实现。拿Protocol举例,它的实现:DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol等等。dubbo的扩展机制如何去查找你的实现类,并实现调用的呢?

ExtensionLoader<T>
拿ServiceConfig<T>中的这两个变量举例子。
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
这两个变量最终生成的既不是接口也不是具体的实现类,而是一个接口适配器类。这个适配器类动态的生成的,如下所示的代码:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
//根据url的信息去获取真实的实现类
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
if (arg2 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}

首先,我们来看适配器类是如何生成

com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);

第一步:通过proxyFactory.class类型生成一个ExtensionLoader<T>实例。

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {每个定义的spi的接口都会构建一个ExtensionLoader实例,存储在EXTENSION_LOADERS
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;}

第二步:创建Adaptive实例,放入数组cachedAdaptiveInstance中。

public T getAdaptiveExtension() {Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {//创建Adaptive实例,放入数组cachedAdaptiveInstance中。instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);}}}}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);}}
return (T) instance;
}private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);}
}private Class<?> createAdaptiveExtensionClass() {
//生成类适配器,String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//编译这个类适配器为class文件
return compiler.compile(code, classLoader);
}
// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {//先读取SPI注解的value值,有值作为默认扩展实现的key
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()+ ": " + Arrays.toString(names));}
if(names.length == 1) cachedDefaultName = names[0];}}Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();//读取三个文件夹下的文件,将key-class放置到extensionClasses中loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);loadFile(extensionClasses, DUBBO_DIRECTORY);loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
//获取文件路径 目录+type名称String fileName = dir + type.getName();try {Enumeration<java.net.URL> urls;//类加载器ClassLoader classLoader = findClassLoader();if (classLoader != null) {urls = classLoader.getResources(fileName);} else {urls = ClassLoader.getSystemResources(fileName);}if (urls != null) {while (urls.hasMoreElements()) {java.net.URL url = urls.nextElement();try {BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));try {String line = null;while ((line = reader.readLine()) != null) {final int ci = line.indexOf('#');if (ci >= 0) line = line.substring(0, ci);line = line.trim();if (line.length() > 0) {try {String name = null;int i = line.indexOf('=');if (i > 0) {name = line.substring(0, i).trim();line = line.substring(i + 1).trim();}if (line.length() > 0) {
//读取到类Class<?> clazz = Class.forName(line, true, classLoader);
//判断实现类是否实现了type接口if (! type.isAssignableFrom(clazz)) {throw new IllegalStateException("Error when load extension class(interface: " +type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");}
//类中是否有方法打了Adaptive注解if (clazz.isAnnotationPresent(Adaptive.class)) {
//将打了Adaptive注解的类放置到cachedAdaptiveClass 中去。if(cachedAdaptiveClass == null) {cachedAdaptiveClass = clazz;}else if (! cachedAdaptiveClass.equals(clazz)) {throw new IllegalStateException("More than 1 adaptive class found: "+ cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName());}} else {//判断该是否有实现类是否存在入参为接口的构造器clazz.getConstructor(type);Set<Class<?>> wrappers = cachedWrapperClasses;if (wrappers == null) {cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();wrappers = cachedWrapperClasses;}//按照装饰器的角色将该类加进来wrappers.add(clazz);} // end of while urls}} catch (Throwable t) {logger.error("Exception when load extension class(interface: " +type + ", description file: " + fileName + ").", t);}
}

适配器中发现这个实现类也是由这个方法生成extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName)

//创建扩展类
instance = createExtension(name);
private T createExtension(String name) {
//加载类Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(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);}//set注入参数injectExtension(instance);//获取包装器类。class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper   class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapperSet<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && wrapperClasses.size() > 0) {for (Class<?> wrapperClass : wrapperClasses) {
//将实例对更新为有包装器类的实例。             instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));}}return instance;} catch (Throwable t) {throw new IllegalStateException("Extension instance(name: " + name + ", class: " +type + ")  could not be instantiated: " + t.getMessage(), t);}
}

dubbo的SPI扩展机制,使得我们给出url就能动态的去获取真实的实现类。获得到真实的实现类,是实现功能的第一步。下面我们来看看spring如何加载到dubbo解析器类,并将dubbo功能收入囊下的。未完待续。

参考文献

http://m.blog.csdn.net/blog/u010311445/41577235

转载于:https://my.oschina.net/zjItLife/blog/530923

dubboSPI机制浅谈相关推荐

  1. java反射机制浅谈

    一.Java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  2. java虚拟机类加载机制浅谈_浅谈Java虚拟机(三)之类加载机制

    在<浅谈Java虚拟机>这篇文章中,我们提到了JVM从操作系统方面来说,可以将其看做是一个进程,分别有类加载器子系统,执行引擎子系统和垃圾收集子系统.这一篇文章就简单的来谈一下类加载器子系 ...

  3. [激励机制]浅谈内部竞争——如何让你的员工玩命干活?

    我是标题党,标题是故意气你的,千万表拍我. 公元2012年12月12号,Clark 拿出所有积蓄创办了一个公司,招了看上去还不错的5个员工组成了一个小型团队.紧接着,摆在他面前的一个很明显的问题就是- ...

  4. C++ RALL机制浅谈

    一.RALL是什么? RAII全称为(Resource Acquisition Is Initialization),即"资源获取就是初始化",是C++语言的一种管理资源.避免泄漏 ...

  5. Java 同步机制浅谈(转贴) http://www.yuanma.org/data/2007/0523/article_2619.htm 中国源码网

    http://www.yuanma.org/data/2007/0523/article_2619.htm Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键 ...

  6. 网络游戏里的奖励机制浅谈

    心理学书上讲人上瘾是因为大脑中的奖励机制,我以身试法,感觉事实并没有这么简单,可能还要加上另外两个重要的因素:身临其境感和习惯性决策机制.奖励机制是核心,身临其境感为奖励提供前提,习惯性决策的养成(依 ...

  7. Android事件分发浅谈

    Android事件分发机制浅谈 前言:可能Android的事件分发对于刚学Android的童鞋来说接触得不多,这样不奇怪.因为刚学的时候,一般人很难注意到或是会选择主动去了解.那么究竟什么是Andro ...

  8. 浅谈JVM(六):方法调用过程

    上一篇: 浅谈JVM(一):Class文件解析 浅谈JVM(二):类加载机制 浅谈JVM(三):类加载器和双亲委派 浅谈JVM(四):运行时数据区 浅谈JVM(五):虚拟机栈帧结构 6.方法调用过程 ...

  9. 浅谈jdk-spi与dubbo-spi

    浅谈jdk-spi与dubbo-spi 关于SPI: 1. jdk-spi示例: 2. dubbo-spi示例: 关于SPI: 关于SPI的概念,此处不再过多撰述,一句话:接口的实现的发现机制 我们直 ...

最新文章

  1. 数据库,傻逼才用外键约束!
  2. 实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2
  3. Hadoop、storm和Spark的区别、比较
  4. 白居易最动人的10句诗
  5. 单身税的时代就要来临,你还没有用Python帮你找一个女朋友吗?
  6. php 压缩html css,PHP实现动态压缩js与css文件的方法
  7. 55.Linux/Unix 系统编程手册(下) -- 文件加锁
  8. java计算机毕业设计服装批发进销存系统MyBatis+系统+LW文档+源码+调试部署
  9. my security / csdn / 2000w / chakaifang.info
  10. 【新知实验室TRTC】
  11. 小学计算机课教师教学笔记,小学信息技术教师读书笔记
  12. weiphp2.0:关于OneThink后台添加密码重置的功能
  13. 用c语言编程写出杨辉三角,用C语言编写杨辉三角
  14. 2022-2027年中国教育云行业市场调研及未来发展趋势预测报告
  15. Linux 创建、删除文件夹
  16. parent.relativePath‘ of POM io.renren:renren-generator:1.0.0
  17. 2018年全国各省市区编号大全(1)
  18. 南理工计算机考研人数,近5年南理工考研报录比
  19. 微软面试题:五个囚犯抓绿豆
  20. Jetson Xavier nx(ubuntu18.04)安装rtl8152网卡驱动和8192网卡驱动

热门文章

  1. css一个盒子里可以装3个图片并排吗_John: CSS浮动与清除浮动属性详解(CSS float clear)...
  2. mysql2800_mysql error 1045 (2800):。。 'root'@'localhost' (using password:YES)
  3. 5渲染判断_先渲染再对焦,KeyShot 深度通道在 Photoshop 中的对接
  4. mysql 备份需要的权限_mysqldump 备份数据库用户所需要的权限
  5. Spring 容器:三种方式解决 Resource leak: ‘applicationContext‘ is never closed 问题
  6. 平板电脑有什么用_除了盖泡面,平板电脑没什么用了
  7. easyui中onchange事件_React中类似Vue的“模板语法”
  8. 华立学院计算机组成原理考试,广东工业大学华立学院计算机组成原理期末复习重点...
  9. mysql分页 disti_MySql查询性能优化
  10. mysql新增阵列df_DF学Mysql(三)——索引操作