dubbo之@Adaptive注解分析
写在前面
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
扩展类的名字分别是apple
和banana
。
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注解分析相关推荐
- dubbo SPI之@SPI、@Adaptive注解, 以及什么时候动态生成$Adaptive代码
本文基于dubbo2.7.7对如下三个问题分析 @SPI注解的作用 @Adaptive注解的作用,放在Type和Method上的区别和注意点 什么时候动态生成和编译xxx$Adaptive代码 @SP ...
- dubbo的@Reference注解作用分析
目的 看看dubbo是怎么给加了@Reference注解的属性注入invoker实例,为什么有时候加了@Reference注解的属性会是null. ReferenceAnnotationBeanPos ...
- Dubbo的Reference注解必须先启动provider的问题
目录 现象 看源码分析原因 注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig 注解Reference第二步:从配置文件里获取参数,写入Referenc ...
- SpringBoot系列三:SpringBoot基本概念(统一父 pom 管理、SpringBoot 代码测试、启动注解分析、配置访问路径、使用内置对象、项目打包发布)...
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.了解SpringBoot的基本概念 2.具体内容 在之前所建立的 SpringBoot 项目只是根据官方文档实现的一个基础程 ...
- @SpringBootApplication注解分析
转自:https://www.cnblogs.com/duanxz/p/3756364.html @SpringBootApplication注解分析 首先我们分析的就是入口类Application的 ...
- Mybatis @Flush注解分析
Mybatis @Flush注解分析 在看源码的的时候,发现了@Flush注解.之前没用过,于是就有了这篇文章 注意:这里的执行器的类型肯定是BatchExecutor 先来例子 @Testpubli ...
- Dubbo学习记录(八) -- Spring整合Dubbo中@Reference注解解析原理
Spring整合Dubbo中@Reference注解解析原理 @Reference: 可以用在属性或者方法, 意味着需要引用某个Dubbo服务, 那么Dubbo整合Spring后, 我很好奇怎么把这个 ...
- Dubbo Spring Cloud 逆向分析服务注册事件变化的处理过程
这篇介绍了如何从接收事件的方法逆向推出完整的事件处理过程,这个方法适合在解决具体问题或学习源码时,倒着把处理过程理顺. 起因 原来用的 Spring Boot + Dubbo 开发架构,在架构中有一个 ...
- dubbo负载均衡代码分析1(leastactive策略)
2019独角兽企业重金招聘Python工程师标准>>> 接上篇https://my.oschina.net/u/146130/blog/1569554 既然有集群容错,自然会有负载均 ...
- dubbo之SPI Wrapper分析
写在前面 本文需要dubbo SPI的简单基础知识,对dubbo SPI不了解的朋友可以参考dubbo之SPI分析 . 源码!!!. 在dubbo之SPI分析 文章中我们分析了SPI机制,其中有种SP ...
最新文章
- C#拾遗系列(4):索引器
- top命令的笔记补充2--如何将top信息后台运行并写入log
- IMAP IDLE模式(推送邮件)
- kuka程序备份_那智机器人系统备份步骤
- DBA用于查询当前数据库表格记录条数的脚本
- 软考系统架构师笔记-案例分析重点(一)
- 程序员如何才配拥有姓名?
- JavaScript的学习--生成二维码
- SAP FICO面试题目+答案
- Android AdapterViewFlipper
- 【渝粤教育】国家开放大学2018年春季 0242-21T机械制图 参考试题
- JavaEE学习04--requestresponse
- Atitit pg10分区 总结 1.1. create table tmp_log ( 	1 1.2. -创建索引	1 1.3. 查看表 in pgadmin4	2 2. 二 分区表管理	2 2.1
- java 锯齿_Java2D图形抗锯齿
- eclipse更改J2EE对应的Web版本
- NES模拟器开发笔记(001)缘起、资料及开发准备
- 【WiFi】WiFi6E 6G 信道与频宽对应关系
- 读书到什么程度才能算融会贯通?
- DSPE-PEG近年来在长循环脂质体、高分子胶束等药物载体中的应用获得了较快发展
- arena of valor服务器未响应,传说对决 -Arena of Valor-启动后一直黑屏无法进入什么原因...
热门文章
- PHP二次元风格发卡系统源码荔枝发卡网
- 服务器关机 正在注销,Win7系统关机一直卡在正在注销如何解决
- tds for mysql_tds数据库是什么-和tds数据库相关的问题-阿里云开发者社区
- c语言航标知识点,书摘:迷惘时的航标——“人生哲学”
- 直播平台搭建源码,css预加载旋转动画 与 流光字体
- 我在富士康挨踢了七年(十六. 跳楼年,终于认识了我自己)
- 服务器ubuntu系统调节亮度,Ubuntu系统下调节屏幕亮度的两种Linux命令
- 商汤科技创业领导_从技术专家到领导者的创业之旅
- 单壁碳纳米管-DNA复合物(SWCNT-DNA)|作用机理
- 用UCWEB浏览器上网 省流量小技巧