写在前面

dubbo提供了SPI机制可以通过外部配置文件来动态加载扩展类,有时我们可能需要配置这些扩展类挨个执行来满足业务场景,进行一些数据的处理等,此时这些扩展类我们都是需要的,还有一些其他的场景,需要根据外部环境的不同(如某参数的值),来动态的选择使用哪个扩展类,针对这种需求,dubbo提供了@Adaptive注解来完成该功能,源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) // 在类和方法上使用
public @interface Adaptive {/**设置注入哪个扩展类。目标扩展类名称通过在URL中传递的参数值确定,而URL中参数的key是什么就是通过该方法的返回值确定的,可以有多个,默认值是接口简单名称转点分形式,如MyInterface就是my.interface。如果是在URL上没有设置目标扩展类名称,则会读取在@SPI注解上配置的值。比如这里配置的值是String[] {"key1", "key2"},首先通过key1从URL上寻找目标值作为扩展类的名字,找不到则使用key2继续寻找,没有找到则使用默认值,即@SPI注解配置的值(没有配置则转换为点分形式作为名称),如果是也没有则会抛出java.lang.IllegalStateException*/String[] value() default {};}

下面看一个简单的例子。

1:简单例子

源码 。

1.1:定义接口和实现类

@SPI("apple")
public interface FruitGranter {Fruit grant();@AdaptiveString watering(URL url);
}

我们定义了一个水果种植类的接口FruitGranter,并通过设置@SPI("apple")来指定该接口为dubbo SPI接口,并设置要使用的默认的扩展类的名称是apple。另外在方法watering中配置了@Adaptive注解,代表需要根据URL中的参数来动态调用哪个扩展类方法,然后定义如下两个实现类:

// 苹果种植者
public class AppleGranter implements FruitGranter {@Overridepublic Fruit grant() {return new Apple();}@Overridepublic String watering(URL url) {System.out.println("watering apple");return "watering finished";}
}
// 香蕉种植者
public class BananaGranter implements FruitGranter {@Overridepublic Fruit grant() {return new Banana();}@Overridepublic String watering(URL url) {System.out.println("watering banana");return "watering success";}
}

定义了香蕉种植者和苹果种植者两个类,然后我们在配置文件中进行配置。

1.2:配置文件

首先在classpath下创建目录META-INF/dubbo,然后创建以FruitGranter全限定名称为文件名的文件,本文文件名称是dongshi.daddy.adaptive.FruitGranter,然后添加苹果扩展类和香蕉扩展类:

apple=dongshi.daddy.adaptive.AppleGranter
banana=dongshi.daddy.adaptive.BananaGranter

扩展类的名字分别是applebanana

1.3:测试

如下测试代码是使用名称为banana扩展类:

public class ExtensionLoaderTest {@Testpublic void testGetExtensionLoader() {// 首先创建一个模拟用的URL对象URL url = URL.valueOf("dubbo://192.168.0.101:20880?fruit.granter=banana");// 通过ExtensionLoader获取一个FruitGranter对象FruitGranter granter = ExtensionLoader.getExtensionLoader(FruitGranter.class).getAdaptiveExtension();// 使用该FruitGranter调用其"自适应标注的"方法,获取调用结果String result = granter.watering(url);System.out.println(result);}
}

运行输出如下:

watering banana
watering success

如果是修改URL url = URL.valueOf("dubbo://192.168.0.101:20880?fruit.granter=banana");URL url = URL.valueOf("dubbo://192.168.0.101:20880");则会使用@SPI("apple")中设置的apple作为默认的扩展类名称,此时输出如下:

watering apple
watering finished

此时从URL上寻找扩展名称的key是FruitGranter接口简单名称的点分形式,即fruit.granter,我们也可以通过在FruitGranter#watering@Adaptive注解中设置key,比如设置为find.fruit.extenstion,如下:

@SPI("apple")
public interface FruitGranter {Fruit grant();//  @Adaptive@Adaptive({ "find.fruit.extenstion" })String watering(URL url);
}

此时测试代码修改为如下也可以获取到名称为banana的扩展类:

public class ExtensionLoaderTest {@Testpublic void testGetExtensionLoader() {// 首先创建一个模拟用的URL对象
//    URL url = URL.valueOf("dubbo://192.168.0.101:20880?fruit.granter=banana");URL url = URL.valueOf("dubbo://192.168.0.101:20880?find.fruit.extenstion=banana");
//    URL url = URL.valueOf("dubbo://192.168.0.101:20880");// 通过ExtensionLoader获取一个FruitGranter对象FruitGranter granter = ExtensionLoader.getExtensionLoader(FruitGranter.class).getAdaptiveExtension();// 使用该FruitGranter调用其"自适应标注的"方法,获取调用结果String result = granter.watering(url);System.out.println(result);}
}

测试输出如下:

watering apple
watering finishedProcess finished with exit code 0

以上的例子,我们是将@Adaptive注解写到接口的方法上,此时dubbo会通过动态代理方式生成接口一个子类,并按照一定的逻辑实现该方法,逻辑其实也很简单,即根据最终获取到的参数值来获取对应的扩展类,并调用其方法,没有添加@Adaptive注解的方法会默认抛出异常信息,如下是本例生成的动态代码:

public class FruitGranter$Adaptive implements org.apache.dubbo.demo.example.eg19.FruitGranter {// 没有标注@Adaptive注解的方法,默认抛出java.lang.UnsupportedOperationException异常public org.apache.dubbo.demo.example.eg19.Fruit grant() {throw new UnsupportedOperationException("The method public abstract org.apache.dubbo.demo.example.eg19.Fruit " + "org.apache.dubbo.demo.example.eg19.FruitGranter.grant() of interface " + "org.apache.dubbo.demo.example.eg19.FruitGranter is not adaptive method!");}// 该方法使用了@Adaptive注解,自动生成动态调用的逻辑 public java.lang.String watering(org.apache.dubbo.common.URL arg0) {// URL参数必须有值if (arg0 == null) {throw new IllegalArgumentException("url == null");}org.apache.dubbo.common.URL url = arg0;// 以fruit.granter作为key从url上获取参数值,默认值是apple,就是在注解@SPI("apple")配置的值String extName = url.getParameter("fruit.granter", "apple");if (extName == null) {throw new IllegalStateException("Failed to get extension (org.apache.dubbo.demo.example.eg19.FruitGranter) name " + "from url (" + url.toString() + ") use keys([fruit.granter])");}// 调用ExtensionLoader的方法根据扩展类名称获取对应的扩展实现类org.apache.dubbo.demo.example.eg19.FruitGranter extension =(org.apache.dubbo.demo.example.eg19.FruitGranter) ExtensionLoader.getExtensionLoader(org.apache.dubbo.demo.example.eg19.FruitGranter.class).getExtension(extName);// 调用目标扩展实现类的watering方法return extension.watering(arg0);}
}

另外,@Adaptive注解还可以使用在接口的子类上,此时会直接执行该子类逻辑,在dubbo中目前只有ExtensionFactory的子类AdaptiveExtensionFactory是这种使用方法,用来分别从SPI和spring容器获取对象。下面我们再看一个将@Adptive注解使用在子类上的例子。

2:@Adaptive注解使用在子类上

源码 。

2.1:定义接口和实现类

2.1.1:接口

@SPI
public interface UseInSubClsInterface {void sayHi(String word);
}

2.1.2:非Adaptive实现类

public class ConcreteUseInSubClsInterface1 implements UseInSubClsInterface {@Overridepublic void sayHi(String word) {System.out.println("ConcreteUseInSubClsInterface1 say hi: " + word);}
}
public class ConcreteUseInSubClsInterface2 implements UseInSubClsInterface {@Overridepublic void sayHi(String word) {System.out.println("ConcreteUseInSubClsInterface2 say hi: " + word);}
}

2.1.3:Adaptive的实现类

@Adaptive
public class AdaptiveUseInSubClsInterface implements UseInSubClsInterface {// 封装所有的扩展类集合(@Adaptive注解的扩展类除外!!!)private final List<UseInSubClsInterface> factories;public AdaptiveUseInSubClsInterface() {// 获取接口UseInSubClsInterface所有的扩展类ExtensionLoader<UseInSubClsInterface> loader= ExtensionLoader.getExtensionLoader(UseInSubClsInterface.class);List<UseInSubClsInterface> list = new ArrayList<UseInSubClsInterface>();for (String name : loader.getSupportedExtensions()) {list.add(loader.getExtension(name));}factories = Collections.unmodifiableList(list);}@Overridepublic void sayHi(String word) {// 任选一个扩展类来调用其方法int size = factories.size();Random random = new Random();factories.get(random.nextInt(size)).sayHi(word);}
}

为了测试目的,只是获取任意一个扩展类来调用。

2.2:定义SPI文件

文件名称dongshi.daddy.adaptive.useinsubcls.UseInSubClsInterface,内容如下:

adaptive=dongshi.daddy.adaptive.useinsubcls.AdaptiveUseInSubClsInterface
concreteUseInSubClsInterface1=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface1
concreteUseInSubClsInterface2=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface2

2.3:测试

public class AdaptiveInSubClsTest {@Testpublic void testGetExtensionLoader() {UseInSubClsInterface useInSubClsInterface= ExtensionLoader.getExtensionLoader(UseInSubClsInterface.class)// 获取Aaptive的子类.getAdaptiveExtension();useInSubClsInterface.sayHi("good night,mongo need to drink milk!!!");}
}

多次运行可以看到会随机调用,多次运行输出如下:

ConcreteUseInSubClsInterface2 say hi: good night,mongo need to drink milk!!!
ConcreteUseInSubClsInterface1 say hi: good night,mongo need to drink milk!!!

写在后面

参考文章列表:

Dubbo Adaptive机制详解

dubbo之@Adaptive注解分析相关推荐

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

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

  2. dubbo的@Reference注解作用分析

    目的 看看dubbo是怎么给加了@Reference注解的属性注入invoker实例,为什么有时候加了@Reference注解的属性会是null. ReferenceAnnotationBeanPos ...

  3. Dubbo的Reference注解必须先启动provider的问题

    目录 现象 看源码分析原因 注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig 注解Reference第二步:从配置文件里获取参数,写入Referenc ...

  4. SpringBoot系列三:SpringBoot基本概念(统一父 pom 管理、SpringBoot 代码测试、启动注解分析、配置访问路径、使用内置对象、项目打包发布)...

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.了解SpringBoot的基本概念 2.具体内容 在之前所建立的 SpringBoot 项目只是根据官方文档实现的一个基础程 ...

  5. @SpringBootApplication注解分析

    转自:https://www.cnblogs.com/duanxz/p/3756364.html @SpringBootApplication注解分析 首先我们分析的就是入口类Application的 ...

  6. Mybatis @Flush注解分析

    Mybatis @Flush注解分析 在看源码的的时候,发现了@Flush注解.之前没用过,于是就有了这篇文章 注意:这里的执行器的类型肯定是BatchExecutor 先来例子 @Testpubli ...

  7. Dubbo学习记录(八) -- Spring整合Dubbo中@Reference注解解析原理

    Spring整合Dubbo中@Reference注解解析原理 @Reference: 可以用在属性或者方法, 意味着需要引用某个Dubbo服务, 那么Dubbo整合Spring后, 我很好奇怎么把这个 ...

  8. Dubbo Spring Cloud 逆向分析服务注册事件变化的处理过程

    这篇介绍了如何从接收事件的方法逆向推出完整的事件处理过程,这个方法适合在解决具体问题或学习源码时,倒着把处理过程理顺. 起因 原来用的 Spring Boot + Dubbo 开发架构,在架构中有一个 ...

  9. dubbo负载均衡代码分析1(leastactive策略)

    2019独角兽企业重金招聘Python工程师标准>>> 接上篇https://my.oschina.net/u/146130/blog/1569554 既然有集群容错,自然会有负载均 ...

  10. dubbo之SPI Wrapper分析

    写在前面 本文需要dubbo SPI的简单基础知识,对dubbo SPI不了解的朋友可以参考dubbo之SPI分析 . 源码!!!. 在dubbo之SPI分析 文章中我们分析了SPI机制,其中有种SP ...

最新文章

  1. C#拾遗系列(4):索引器
  2. top命令的笔记补充2--如何将top信息后台运行并写入log
  3. IMAP IDLE模式(推送邮件)
  4. kuka程序备份_那智机器人系统备份步骤
  5. DBA用于查询当前数据库表格记录条数的脚本
  6. 软考系统架构师笔记-案例分析重点(一)
  7. 程序员如何才配拥有姓名?
  8. JavaScript的学习--生成二维码
  9. SAP FICO面试题目+答案
  10. Android AdapterViewFlipper
  11. 【渝粤教育】国家开放大学2018年春季 0242-21T机械制图 参考试题
  12. JavaEE学习04--requestresponse
  13. Atitit pg10分区 总结 1.1. create table tmp_log (  1 1.2. -创建索引 1 1.3. 查看表 in pgadmin4 2 2. 二 分区表管理 2 2.1
  14. java 锯齿_Java2D图形抗锯齿
  15. eclipse更改J2EE对应的Web版本
  16. NES模拟器开发笔记(001)缘起、资料及开发准备
  17. 【WiFi】WiFi6E 6G 信道与频宽对应关系
  18. 读书到什么程度才能算融会贯通?
  19. DSPE-PEG近年来在长循环脂质体、高分子胶束等药物载体中的应用获得了较快发展
  20. arena of valor服务器未响应,传说对决 -Arena of Valor-启动后一直黑屏无法进入什么原因...

热门文章

  1. PHP二次元风格发卡系统源码荔枝发卡网
  2. 服务器关机 正在注销,Win7系统关机一直卡在正在注销如何解决
  3. tds for mysql_tds数据库是什么-和tds数据库相关的问题-阿里云开发者社区
  4. c语言航标知识点,书摘:迷惘时的航标——“人生哲学”
  5. 直播平台搭建源码,css预加载旋转动画 与 流光字体
  6. 我在富士康挨踢了七年(十六. 跳楼年,终于认识了我自己)
  7. 服务器ubuntu系统调节亮度,Ubuntu系统下调节屏幕亮度的两种Linux命令
  8. 商汤科技创业领导_从技术专家到领导者的创业之旅
  9. 单壁碳纳米管-DNA复合物(SWCNT-DNA)|作用机理
  10. 用UCWEB浏览器上网 省流量小技巧