建议先阅读上一篇文章Dubbo之@SPI,效果更佳。

关键词:@Adaptive


百度一下adaptive,翻译是这样的:

个人觉得结合Dubbo中的@Adaptive的功能,合适的翻译是能适应的。而@Adaptive真的做到了根据参数能适应地返回不同的结果对象。


█ 如何使用

@Adaptive,可以在两个地方使用:接口方法上或者接口的实现类上。

①修改AnimalService接口,添加两个方法。在方法上标注@Adaptive注解。

package study.rui.dubbo;import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;@SPI
public interface AnimalService {@Adaptive({"firstName"})void show(URL url);@Adaptive({"lastName"})void run(Animal animal);void say();}

第二个方法用到了一个Animal类,Animal的代码如下:

package study.rui.dubbo;import com.alibaba.dubbo.common.URL;public class Animal {private URL url;public void setUrl(URL url) {this.url = url;}public URL getUrl() {return url;}}

②修改DogService类,并且新增一个接口实现类CatService。在配置文件中添加CatService的配置。

package study.rui.dubbo.impl;import com.alibaba.dubbo.common.URL;
import study.rui.dubbo.Animal;
import study.rui.dubbo.AnimalService;public class DogService implements AnimalService {@Overridepublic void say() {System.out.println("dog say woo woo");}@Overridepublic void show(URL url) {System.out.println("dog" + url.toString());}@Overridepublic void run(Animal animal) {System.out.println("dog" + animal.getUrl().toString());}
}
package study.rui.dubbo.impl;import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import study.rui.dubbo.Animal;
import study.rui.dubbo.AnimalService;@Adaptive
public class CatService implements AnimalService {@Overridepublic void say() {System.out.println("cat say miao miao");}@Overridepublic void show(URL url) {System.out.println("cat" + url.toString());}@Overridepublic void run(Animal animal) {System.out.println("cat" + animal.getUrl().toString());}
}
dog=study.rui.dubbo.impl.DogService
cat=study.rui.dubbo.impl.CatService

③运行代码。

import com.alibaba.dubbo.common.extension.ExtensionLoader;
import study.rui.dubbo.AnimalService;public class AdaptiveTest {public static void main(String[] args) {ExtensionLoader<AnimalService> loader = ExtensionLoader.getExtensionLoader(AnimalService.class);// 获取到CatService实例AnimalService animalService = loader.getAdaptiveExtension();// 返回study.rui.dubbo.impl.CatServiceSystem.out.println(animalService.getClass().getName());}}

对于③中的运行结果,为什么getAdaptiveExtension()返回的是CatService实例呢。因为在CatService上标注了@Adaptive注解。

// 获取能适应的扩展类Class
private Class<?> getAdaptiveExtensionClass() {this.getExtensionClasses();// cachedAdaptiveClass不为null,则直接返回cachedAdaptiveClass,后面创建cachedAdaptiveClass类实例// cachedAdaptiveClass为空,则通过createAdaptiveExtensionClass方法动态生成一个实例。return this.cachedAdaptiveClass != null ? this.cachedAdaptiveClass : (this.cachedAdaptiveClass = this.createAdaptiveExtensionClass());
}

cachedAdaptiveClass什么时候是null,什么时候又不是null呢。在ExtensionLoaderloadClass方法中:

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {......// 如果clazz上标注了@Adaptive注解,clazz是接口的实现类的Class对象。// 例子中就是DogService.class和CatService.classif (clazz.isAnnotationPresent(Adaptive.class)) {// CatService进入if (this.cachedAdaptiveClass == null) {this.cachedAdaptiveClass = clazz;} // 这里规定了接口的实现类中,有且仅有一个实现类能够标注@Adaptiveelse if (!this.cachedAdaptiveClass.equals(clazz)) {throw new IllegalStateException("More than 1 adaptive class found: " + this.cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());}} ......
}

④去掉CatService上的@Adaptive。

⑤运行代码。

public class AdaptiveTest {public static void main(String[] args) {ExtensionLoader<AnimalService> loader = ExtensionLoader.getExtensionLoader(AnimalService.class);AnimalService animalService = loader.getAdaptiveExtension();URL url = new URL("http", "localhost", 80);// 这里要特别注意addParameter方法// 如果url的参数中有了firstName,并且firstName的值就是cat,则会返回当前url实例// 否则会重新new 一个URL对象的URL newUrl = url.addParameter("firstName", "cat");// 结果打印的是CatService.show方法中的内容// 你知道为什么吗?animalService.show(newUrl);}}

█ 总结说明 

还记得上面说明篇说的吗,当cachedAdaptiveClass==null的时候,会返回createAdaptiveExtensionClass这个方法的结果。而当没有一个实现类上标注@Adaptive时,cachedAdaptiveClass就为null了。下面我们进入createAdaptiveExtensionClass冒个险。

private Class<?> createAdaptiveExtensionClass() {// 这里就是创建类的字符串的String code = this.createAdaptiveExtensionClassCode();ClassLoader classLoader = findClassLoader();// 又是getAdaptiveExtension,仔细想想这里会获取到哪个类的实例呢?// 提示:看看接口Compiler的实现类中是否有标注了@Adaptive,如果有就是他了// 其实想想一定会有实现类标注注解的。不然又会进入createAdaptiveExtensionClass方法,这不死循环了嘛。Compiler compiler = (Compiler)getExtensionLoader(Compiler.class).getAdaptiveExtension();// 根据字符串生成Class对象return compiler.compile(code, classLoader);
}

大家还是否记得,一开始我说的@Adaptive只能作用于两个地方。标注在实现类上和标注在接口方法上。既然标注在实现类上是不会进到这个方法里面的,那就表示标注在接口方法的时候就会进入这个方法喽。其实createAdaptiveExtensionClassCode方法,就是根据接口方法上的注解信息动态产生代码的。有兴趣的同学可以看看这个方法的源码。我这里就通过方法将createAdaptiveExtensionClassCode生成的类字符串打印出来给大家看看。

createAdaptiveExtensionClassCode这个方法是私有的,我们没法直接调用它,不过可以通过反射来调用。编写测试方法。

ExtensionLoader<AnimalService> extensionLoader = ExtensionLoader.getExtensionLoader(AnimalService.class);
Class<? extends ExtensionLoader> extensionLoaderClass = extensionLoader.getClass();
Method method = extensionLoaderClass.getDeclaredMethod("createAdaptiveExtensionClassCode", null);
method.setAccessible(true);
Object result = method.invoke(extensionLoader, null);
System.out.println(result);

结果如下。下面的例子只是Dubbo根据AnimalService方法生成的类,并不是所有的类的内容都是相同的。Dubbo会动态判断动态生成不同的结果。

// 包路径和接口的包路径相同
package study.rui.dubbo;import com.alibaba.dubbo.common.extension.ExtensionLoader;// 类名就是接口名加$Adaptive
public class AnimalService$Adaptive implements study.rui.dubbo.AnimalService {public void run(study.rui.dubbo.Animal arg0) {// 前置校验,参数不能为空if (arg0 == null)throw new IllegalArgumentException("study.rui.dubbo.Animal argument == null");// 从参数中获取URL对象if (arg0.getUrl() == null)throw new IllegalArgumentException("study.rui.dubbo.Animal argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();// 从URL中获取参数lastName,为什么是lastName呢?// 因为在注解中设置了值 @Adaptive({"lastName"})String extName = url.getParameter("lastName");// URL中没有指定参数或者指定参数的值为空,抛异常if(extName == null)throw new IllegalStateException("Fail to get extension(study.rui.dubbo.AnimalService) name from url(" + url.toString() + ") use keys([lastName])");// 根据值获取对应的扩展实现类对象study.rui.dubbo.AnimalService extension = (study.rui.dubbo.AnimalService)ExtensionLoader.getExtensionLoader(study.rui.dubbo.AnimalService.class).getExtension(extName);// 调用具体实现类的对象的方法extension.run(arg0);}public void show(com.alibaba.dubbo.common.URL arg0) {if (arg0 == null)throw new IllegalArgumentException("url == null");// 如果参数就是URL类型的com.alibaba.dubbo.common.URL url = arg0;String extName = url.getParameter("firstName");if(extName == null)throw new IllegalStateException("Fail to get extension(study.rui.dubbo.AnimalService) name from url(" + url.toString() + ") use keys([firstName])");study.rui.dubbo.AnimalService extension = (study.rui.dubbo.AnimalService)ExtensionLoader.getExtensionLoader(study.rui.dubbo.AnimalService.class).getExtension(extName);extension.show(arg0);}public void say() {// 没有标注@Adaptive注解的方法调用就会直接抛异常throw new UnsupportedOperationException("method public abstract void study.rui.dubbo.AnimalService.say() of interface study.rui.dubbo.AnimalService is not adaptive method!");}
}

①@Adaptive修饰的方法,这个方法的参数必须有一个类型是com.alibaba.dubbo.common.URL,或者该类型中含有一个返回值是URL,访问修饰符是public,不是静态方法,无参的、(get开头或方法名长度大于3)的方法。如Animal。将Animal改成下面几种情况都是可以当做参数的。Dubbo能够校验通过,并且正确解析生成类字符串。

package study.rui.dubbo;import com.alibaba.dubbo.common.URL;public class Animal {public URL getUrl() {return null;}}
package study.rui.dubbo;import com.alibaba.dubbo.common.URL;public class Animal {public URL get() {return null;}}
package study.rui.dubbo;import com.alibaba.dubbo.common.URL;public class Animal {public URL hhhh() {return null;}}
package study.rui.dubbo;import com.alibaba.dubbo.common.URL;
/*** 像这种有两个方法都返回URL的* 遍历所有方法,先获取到谁,就用谁去获取URL*/
public class Animal {public URL hhhh() {return null;}public URL hhhh2() {return null;}}

②@Adaptive修饰的方法,符合上面条件的参数可以有多个。但只会用第一个符合条件的参数。

// 代码中只会使用第一个参数url,url.getParamter
@Adaptive({"firstName"})
void show(URL url, URL url2);

③在@Adaptive中没有设置value的时候,Dubbo会拿到接口名根据驼峰模式,遇到大写字母加点号,将所有字母变小写。如AnimalService会生成animal.service这样的值,然后通过url.getParameter("animal.service")获取。

④@Adaptive中可以指定多个值。按照顺序依次从URL中获取参数,直到获取到值位置。

// 依次从URL中获取参数名为lastName、firstName、age
// 获取到的参数值不为空就停止向后获取了。即,url.getParamter("lastName")不为空的话
// 就不会去获取lastName和age的参数值了
@Adaptive({"lastName", "firstName", "age"})
void run(Animal animal);

欢迎评论指导,留下您宝贵的建议或意见,谢谢!

Dubbo之@Adaptive相关推荐

  1. dubbo服务发布一之服务暴露

    整体流程以调试 om.alibaba.dubbo.demo.provider.DemoProvider来演示dubbo服务的发布流程. 1.启动Spring容器 参照dubbo容器的启动, https ...

  2. Dubbo常见面试题与答案

    Dubbo的基础知识 Dubbo的核心架构是怎样的? Registry:注册中心. 负责服务地址的注册与查找,服务的 Provider 和 Consumer 只在启动时与注册中心交互.注册中心通过长连 ...

  3. Dubbo - Dubbo的SPI机制

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

  4. Dubbo SPI机制和原理解析

    简介 SPI(service provider interface)是一种服务发现机制,通过加载指定路径下配置文件中的实现类,达到运行时用实现动态替换接口的目的.SPI常常用于扩展应用的功能,Dubb ...

  5. Dubbo之@Activate

    建议先阅读上一篇文章Dubbo之@Adaptive,效果更佳. 关键词:@Activate 在<Dubbo之@SPI>和<Dubbo之@Adaptive>中分别介绍了@SPI和 ...

  6. dubbo源码解析-SPI机制

    SPI,Service Provider Interface,服务提供者接口,是一种服务发现机制. JDK 的 SPI 规范 JDK 的 SPI 规范规定:  接口名:可随意定义  实现类名:可随 ...

  7. 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解

    本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...

  8. dubbo SPI使用:@SPI 与 @Adaptive

    dubbo中使用spi除了配置文件外还需要有@SPI和@Adaptive注解,这两个注解中都可以设置参数,且@Adaptive注解可以设置在方法上也可以设置在类上.下面我们来卡一下他的具体使用规则. ...

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

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

  10. dubbo SPI之@SPI、@Adaptive注解, 以及什么时候动态生成$Adaptive代码

    本文基于dubbo2.7.7对如下三个问题分析 @SPI注解的作用 @Adaptive注解的作用,放在Type和Method上的区别和注意点 什么时候动态生成和编译xxx$Adaptive代码 @SP ...

最新文章

  1. ssl charles 参数看不到_SSL证书=安全?小心,别错漏了TA……
  2. SAP Fiori Elements edit按钮的ABAP端实现细节
  3. matlab销量预测的数学模型,数学建模:酒店最优化问题.用matlab算出《酒店价格预测模型》...
  4. 智能搜索推荐模型预估框架的建设及在美团点评的实践
  5. 001-为什么Java能这么流行
  6. VistaNet: Visual Aspect Attention Network for Multimodal Sentiment Analysis 论文笔记
  7. linux监控文件是否传输,利用SecureCRT在linux与Windows之间传输文件
  8. ddr3ddr4 lpddr4速率_LPDDR3一定弱?实测对比单双通道DDR4
  9. php博饼,妙趣横生庆中秋:厦门博饼
  10. 三阶魔方还原步骤图_三阶魔方七步还原法口诀,魔方新手入门图解步骤
  11. Win10(Win7)局域网设置共享文件夹,超全面步骤。
  12. 查看自己电脑外网IP
  13. 有线网口设备转为无线wifi,RJ45网口转wifi,即插即用,网卡转无线wifi完全透传
  14. win10连接共享打印机_共享打印机的三种安装连接方法
  15. Android华为HiAI语音识别的集成与使用
  16. 小米 11 Ultra 正式发布,自称 “安卓之光”
  17. 图说区块链 神一样的金融科技与未来社会
  18. openbmc开发16:配置文件详细介绍
  19. Java训练work3.Exer2---跳跃最大长度
  20. 使用IDEA从零开始新建一个springboot项目

热门文章

  1. 如果已经安装过个人版Delphi2007,如何安装Delphi2007企业版
  2. Python学习随笔:PyCharm的错误检测使用及调整配置减少错误数量
  3. Springboot毕设项目基于批示的督查督办管理系统c6m0djava+VUE+Mybatis+Maven+Mysql+sprnig)
  4. 明天(10分)C语言
  5. 时间紧、任务重、资源有限,项目经理如何来保证研发效率?
  6. 滑动t检验在matlab上的实现
  7. 阿里智能App下架,智能家居平台淘汰赛拉开大幕
  8. Lambda将搬砖变成艺术
  9. 训练faster rcnn报错:KeyError:‘max_overlaps’
  10. 低光图像增强(Low-light image enhancement)文章整理