dubbo 的SPI机制Adaptive适配
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 注解的value作为 扩展点名称
- 扩展点实现类添加Adaptive注解时候在所有扩展点中优先级最高
dubbo 的SPI机制Adaptive适配相关推荐
- Dubbo的SPI机制对比传统的SPI做了哪些改进?Dubbo的IOC和AOP
文章目录 1. JAVA的SPI机制 2. Dubbo的SPI机制 3. Dubbo的SPI源码解析 3.1 获取接口对应的 ExtensionLoader 3.2 根据入参的http获取对应的htt ...
- Dubbo - Dubbo的SPI机制
国庆期间闲来无事,写了一个简单的小程序,小程序名称叫做 IT藏经楼.目的是分享这些年自己积累的一些学习材料,方面大家查找使用,包括电子书.案例项目.学习视频.面试题和一些PPT模板.里面所有材料都免费 ...
- Dubbo服务Spi机制和原理
什么是Dubbo的spi机制? SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置 ...
- 阿里面试真题:Dubbo的SPI机制
点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...
- Dubbo源码分析系列-深入Dubbo SPI机制
导语 在之前的博客中介绍过关于Java中SPI的机制,也简单的分析了关于Java中SPI怎么去使用.SPI的全称Service Provider Interface,是一种服务发现机制.SPI的本 ...
- Dubbo源码解析-——SPI机制
文章目录 一.什么是SPI机制 二.Java原生的SPI机制 2.1.javaSPI示例 2.1.1.编写接口和实现类 2.1.2.编写配置文件 2.1.3.通过SPI机制加载实现类 2.1.4.JA ...
- 【Dubbo原理】(一)Dubbo的微内核架构及SPI机制
文章目录 Dubbo的微内核架构及SPI机制 1.什么是微内核架构 1.1.基本架构 1.2.设计关键点 2.Dubbo中的微内核架构 2.1.Dubbo的分层 2.2.Dubbo的插件模块 2.3. ...
- Dubbo SPI机制学习总结(持续更新...)
参考文章:Dubbo的SPI机制分析 首先来看看 Java SPI 的机制 Java SPI 起初是提供给厂商做插件开发用的,例如数据库驱动java.sql.Driver,市面上各种各样的数据库,不同 ...
- Dubbo系列之自适应(Adaptive)拓展点
Dubbo系列之自定义SPI协议拓展点_codain的博客-CSDN博客Dubbo系列之自定义SPI协议拓展点https://blog.csdn.net/qq_38377525/article/det ...
- 分布式面试 - dubbo 的 spi 思想是什么?
分布式面试 - dubbo 的 spi 思想是什么? 面试题 dubbo 的 spi 思想是什么? 面试官心理分析 继续深入问呗,前面一些基础性的东西问完了,确定你应该都 ok,了解 dubbo 的一 ...
最新文章
- Yii2 HOW-TO(2):最佳实践(1)
- SparkStreaming读取Kakfa数据时发生OffsetOutOfRangeException异常
- 用python玩转数据第四周答案_用Python玩转数据_章节答案
- drf1 rest restful规范
- 【Nutch基础教程之七】Nutch的2种运行模式:local及deploy
- 理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用
- SpringBoot配置logback日志 (六)
- 顶点计划 抄作业问题讨论
- Virtualbox安装xp后网络不通
- SiteApp转码声明 来自百度
- 恒生与中国信通院联合发布《证券行业分布式核心系统SRE运维白皮书》
- 虚拟机终端输入sudo的密码时,无法输入密码
- 【FreeRTOS】
- 安卓刷量技术揭秘(一) 工具篇
- 全网爆火五款高性能旗舰蓝牙耳机推荐,平价高音质蓝牙耳机
- mysql-mmm 故障_mysql-mmm故障解决一例
- SpringCloud从入门到精通(超详细文档)
- 数据中心常用部分英文缩写
- Qt编译通过,运行时出现the process was ended forcefully问题的解决方案
- php.bak是什么,bak文件是什么
热门文章
- 通过surfer提取边界bln文件的方法
- 如何写论文的report(一)
- Android UpdateApk 增量更新
- 数据分析学习总结笔记06:T检验的原理和步骤
- CCS导入工程时报错“overlaps the location of another project”解决办法
- vue 使用高德地图插件 vue-amap
- 购物车结算页面案例jQuery
- spyder python调试_Spyder如何调试
- Unity 性能优化之合批
- 选择与循环:剪刀石头布_剪刀石头布十大奢侈家具,创造高端精致生活就是这么简单!...