SPI机制和Adaptive适配机制

Adaptive适配机制

我们可以使用dubbo的SPI机制, 将dubbo中的一些扩展点通过注解改变原有的实现SPI(“dubbo”), 除此之外Adaptive适配机制则可以帮助我们从参数级别对dubbo的扩展点做出改变。

接口

@SPI("dubbo")
public interface AdaptiveExt2 {@Adaptive()String echo(String msg, URL url);
}

实现类

public class DubboAdaptiveExt2 implements AdaptiveExt2 {@Overridepublic String echo(String msg, URL url) {return "DubboAdaptiveExt2";}
}public class SpringCloudAdaptiveExt2 implements AdaptiveExt2 {@Overridepublic String echo(String msg, URL url) {return "spring cloud";}
}

测试

public class Test {public static void main(String[] args) {ExtensionLoader<AdaptiveExt2> loader = ExtensionLoader.getExtensionLoader(AdaptiveExt2.class);AdaptiveExt2 adaptiveExtension = loader.getAdaptiveExtension();URL url = URL.valueOf("test://localhost/test");System.out.println(adaptiveExtension.echo("d", url));}
}

探究

getAdaptiveExtension

     public T getAdaptiveExtension() {Object instance = cachedAdaptiveInstance.get();// 缓存中没有if (instance == null) {if (createAdaptiveInstanceError == null) {synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();if (instance == null) {try {// 创建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;}

createAdaptiveExtension

    private T createAdaptiveExtension() {try {// 主要是 getAdaptiveExtensionClass()return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);}}

getAdaptiveExtensionClass

    private Class<?> getAdaptiveExtensionClass() {// 得到扩展点的类, 此方法和SPI中加载扩展点信息相同getExtensionClasses();// cachedAdaptiveClass 在 SPI的分析中介绍过了,在加载扩展点信息时候,如果发现有类上包含Adaptiva注解,则使用 cachedAdaptiveClass 缓存。if (cachedAdaptiveClass != null) {// 直接返回, 由此可见如果在类上添加Adaptiva注解,则优先使用return cachedAdaptiveClass;}// 扩展点类上都没 Adaptiva注解, 则去使用代理方式生成return cachedAdaptiveClass = createAdaptiveExtensionClass();}

getExtensionClasses

加载dubbo的扩展信息

    private Map<String, Class<?>> getExtensionClasses() {Map<String, Class<?>> classes = cachedClasses.get();if (classes == null) {synchronized (cachedClasses) {classes = cachedClasses.get();if (classes == null) {classes = loadExtensionClasses();cachedClasses.set(classes);}}}return classes;}

createAdaptiveExtensionClass

创建适配扩展点类, 主要三步

  • 生成类信息,使用String
  • 得到类加载器
  • 编译类
    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();return compiler.compile(code, classLoader);}

生成类信息

在这里方法中,我们主要观察它生成的类信息

    private String createAdaptiveExtensionClassCode() {        StringBuilder codeBuilder = new StringBuilder();        // 得到类信息        Method[] methods = type.getMethods();        // 是否有适配注解        boolean hasAdaptiveAnnotation = false;        for (Method m : methods) {            if (m.isAnnotationPresent(Adaptive.class)) {                hasAdaptiveAnnotation = true;                break;            }        }        // 没有需要适配注解 直接报错        if (!hasAdaptiveAnnotation)            throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");     // 生成基本包名等信息        codeBuilder.append("package ").append(type.getPackage().getName()).append(";");        codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");        codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");              // 遍历所有方法        for (Method method : methods) {            Class<?> rt = method.getReturnType();            Class<?>[] pts = method.getParameterTypes();            Class<?>[] ets = method.getExceptionTypes();          // 拿到方法上的Adaptive注解信息            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);            StringBuilder code = new StringBuilder(512);            if (adaptiveAnnotation == null) {                code.append("throw new UnsupportedOperationException(\"method ")                        .append(method.toString()).append(" of interface ")                        .append(type.getName()).append(" is not adaptive method!\");");            } else {                // 当前方法上包含 Adaptive 注解               // 省略多余代码                String[] value = adaptiveAnnotation.value();                // 没有值, 使用类名作为默认值                if (value.length == 0) {                    char[] charArray = type.getSimpleName().toCharArray();                    StringBuilder sb = new StringBuilder(128);                    for (int i = 0; i < charArray.length; i++) {                        if (Character.isUpperCase(charArray[i])) {                            if (i != 0) {                                sb.append(".");                            }                            sb.append(Character.toLowerCase(charArray[i]));                        } else {                            sb.append(charArray[i]);                        }                    }                    value = new String[]{sb.toString()};                }               // 省略代码             // cachedDefaultName 是SPI注解中的 value, 下面的循环主要是生成了一段代码                // String extName = url.getParameter( key, value );                - key 是方法上 Adaptive 注解的内容,如果没有的话, 使用类名                - value 使用的 defaultExtName (SPI注解的value)值                String defaultExtName = cachedDefaultName;                String getNameCode = null;                for (int i = value.length - 1; i >= 0; --i) {                    if (i == value.length - 1) {                        if (null != defaultExtName) {                            if (!"protocol".equals(value[i]))                                if (hasInvocation)                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);                                else                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);                            else                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);                        } else {                            if (!"protocol".equals(value[i]))                                if (hasInvocation)                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);                                else                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);                            else                                getNameCode = "url.getProtocol()";                        }                    } else {                        if (!"protocol".equals(value[i]))                            if (hasInvocation)                                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);                            else                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);                        else                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);                    }                }         // 省略代码        return codeBuilder.toString();    }

createAdaptiveExtensionClassCode 生成的类信息

package com.weimob.spi;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class AdaptiveExt2$Adaptive implements com.weimob.spi.AdaptiveExt2 { public java.lang.String echo(java.lang.String arg0, com.alibaba.dubbo.common.URL arg1) {        if (arg1 == null)             throw new IllegalArgumentException("url == null");      com.alibaba.dubbo.common.URL url = arg1;       String extName = url.getParameter("adaptive.ext2", "dubbo");       if(extName == null)           throw new IllegalStateException("Fail to get extension(com.weimob.spi.AdaptiveExt2) name from url(" + url.toString() + ") use keys([adaptive.ext2])");        // 可以看到这里使用的是SPI的内容,按照扩展类的名称获取扩展点信息        // extName 的来源和SPI注解、Adaptive注解相关     com.weimob.spi.AdaptiveExt2 extension = (com.weimob.spi.AdaptiveExt2)ExtensionLoader.getExtensionLoader(com.weimob.spi.AdaptiveExt2.class).getExtension(extName);      return extension.echo(arg0, arg1);  }}    public String getParameter(String key, String defaultValue) {        // 去获取key对应的value (URL参数是key?value格式)        String value = getParameter(key);        if (value == null || value.length() == 0) {            // value 为空,使用默认值            return defaultValue;        }        return value;    }

得到信息

  • Adaptive适配机制主要功能是得到扩展点的名称

    • 使用 Adaptive 注解的value作为 扩展点名称

      • value为空时候,选择类名为参数,从URL入参中获取
    • 使用 SPI 注解的value作为 扩展点名称的默认名称
      • 无 Adaptive 注解时,或者按照Adaptive 注解获取不到信息时候
  • 扩展点实现类添加Adaptive注解时候在所有扩展点中优先级最高

dubbo 的SPI机制Adaptive适配相关推荐

  1. Dubbo的SPI机制对比传统的SPI做了哪些改进?Dubbo的IOC和AOP

    文章目录 1. JAVA的SPI机制 2. Dubbo的SPI机制 3. Dubbo的SPI源码解析 3.1 获取接口对应的 ExtensionLoader 3.2 根据入参的http获取对应的htt ...

  2. Dubbo - Dubbo的SPI机制

    国庆期间闲来无事,写了一个简单的小程序,小程序名称叫做 IT藏经楼.目的是分享这些年自己积累的一些学习材料,方面大家查找使用,包括电子书.案例项目.学习视频.面试题和一些PPT模板.里面所有材料都免费 ...

  3. Dubbo服务Spi机制和原理

    ​ 什么是Dubbo的spi机制? SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置 ...

  4. 阿里面试真题:Dubbo的SPI机制

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  5. Dubbo源码分析系列-深入Dubbo SPI机制

    导语   在之前的博客中介绍过关于Java中SPI的机制,也简单的分析了关于Java中SPI怎么去使用.SPI的全称Service Provider Interface,是一种服务发现机制.SPI的本 ...

  6. Dubbo源码解析-——SPI机制

    文章目录 一.什么是SPI机制 二.Java原生的SPI机制 2.1.javaSPI示例 2.1.1.编写接口和实现类 2.1.2.编写配置文件 2.1.3.通过SPI机制加载实现类 2.1.4.JA ...

  7. 【Dubbo原理】(一)Dubbo的微内核架构及SPI机制

    文章目录 Dubbo的微内核架构及SPI机制 1.什么是微内核架构 1.1.基本架构 1.2.设计关键点 2.Dubbo中的微内核架构 2.1.Dubbo的分层 2.2.Dubbo的插件模块 2.3. ...

  8. Dubbo SPI机制学习总结(持续更新...)

    参考文章:Dubbo的SPI机制分析 首先来看看 Java SPI 的机制 Java SPI 起初是提供给厂商做插件开发用的,例如数据库驱动java.sql.Driver,市面上各种各样的数据库,不同 ...

  9. Dubbo系列之自适应(Adaptive)拓展点

    Dubbo系列之自定义SPI协议拓展点_codain的博客-CSDN博客Dubbo系列之自定义SPI协议拓展点https://blog.csdn.net/qq_38377525/article/det ...

  10. 分布式面试 - dubbo 的 spi 思想是什么?

    分布式面试 - dubbo 的 spi 思想是什么? 面试题 dubbo 的 spi 思想是什么? 面试官心理分析 继续深入问呗,前面一些基础性的东西问完了,确定你应该都 ok,了解 dubbo 的一 ...

最新文章

  1. Yii2 HOW-TO(2):最佳实践(1)
  2. SparkStreaming读取Kakfa数据时发生OffsetOutOfRangeException异常
  3. 用python玩转数据第四周答案_用Python玩转数据_章节答案
  4. drf1 rest restful规范
  5. 【Nutch基础教程之七】Nutch的2种运行模式:local及deploy
  6. 理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用
  7. SpringBoot配置logback日志 (六)
  8. 顶点计划 抄作业问题讨论
  9. Virtualbox安装xp后网络不通
  10. SiteApp转码声明 来自百度
  11. 恒生与中国信通院联合发布《证券行业分布式核心系统SRE运维白皮书》
  12. 虚拟机终端输入sudo的密码时,无法输入密码
  13. 【FreeRTOS】
  14. 安卓刷量技术揭秘(一) 工具篇
  15. 全网爆火五款高性能旗舰蓝牙耳机推荐,平价高音质蓝牙耳机
  16. mysql-mmm 故障_mysql-mmm故障解决一例
  17. SpringCloud从入门到精通(超详细文档)
  18. 数据中心常用部分英文缩写
  19. Qt编译通过,运行时出现the process was ended forcefully问题的解决方案
  20. php.bak是什么,bak文件是什么

热门文章

  1. 通过surfer提取边界bln文件的方法
  2. 如何写论文的report(一)
  3. Android UpdateApk 增量更新
  4. 数据分析学习总结笔记06:T检验的原理和步骤
  5. CCS导入工程时报错“overlaps the location of another project”解决办法
  6. vue 使用高德地图插件 vue-amap
  7. 购物车结算页面案例jQuery
  8. spyder python调试_Spyder如何调试
  9. Unity 性能优化之合批
  10. 选择与循环:剪刀石头布_剪刀石头布十大奢侈家具,创造高端精致生活就是这么简单!...