dubbo源码分析-ExtensionLoader发现机制和Adaptive注解应用

Dubbo的灵活性体现在每个系统功能点都可以动态扩展为新的实现,而且只需要额外配置,不需要修改dubbo源码,非常符合面向对象设计的开闭原则,其实现原理利用了JDK5.0的自动发现机制,具体使用相关代码是ExtensionLoader

本文目的:对Dubbo使用ExtensionLoader动态加载扩展点相关源码实现介绍

面向读者:要求对dubbo的扩展点ExtensionLoader使用有基本的了解

背景介绍:官网链接-扩展点加载
- 扩展点配置
- 扩展点自适应

示例代码:来源于Dubbo源码,Git链接

相关术语:
扩展点 Extension - 可以理解为扩展某功能接口的实现类

关于扩展点配置

Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来。具体在Dubbo应用实现如下。

服务加载配置文件位于下面目录:
这里写图片描述

代码能找到配置文件,是由下面相关代码定义的:

public class ExtensionLoader<T> {
    private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
    private static final String SERVICES_DIRECTORY = "META-INF/services/";
    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

如何根据扩展名加载对应实现类

具体例子:下面就是根据扩展名“mymock”加载对应类MockFilter:

 MockFilter filter = (MockFilter)ExtensionLoader.getExtensionLoader(Filter.class).getExtension("mymock");

“mymock”就是指定名字,定义在配置文件内:
mymock=com.alibaba.dubbo.config.spring.filter.MockFilter

其中这里的约定:MockFilter是接口Filter的实现类,文件名
com.alibaba.dubbo.rpc.Filter意思是接口类Filter在包com.alibaba.dubbo.rpc下
目录结构:
这里写图片描述


Dubbo注解@Adaptive用法

-加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,ExtensionLoader在会自动注入依赖的扩展点。
-ExtensionLoader通过扫描扩展点实现类的所有set方法来判定其成员。
即ExtensionLoader会执行扩展点的拼装操作。
-ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用是一个扩展点实现。

1)Adaptive注解定义:

...
public @interface Adaptive {
...
    String[] value() default {};
}

2)接口例子

@SPI("netty")
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;
...
...
}

这些注解在代码中如何发挥作用?


ExtensionLoader的createAdaptiveExtensionClassCode()方法根据注解@Adaptive的key值注入对应实现类。

● 首先知道ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用是哪一个扩展点实现。

● Dubbo使用URL对象(包含了Key-Value)传递配置信息。key是在配置xml里的定义使用,例如xml指定server = value,则key是‘server’, 然后Adaptive实例代码中指定加载的扩展名extName为value,根据此加载对应实现类。

几点注意:
1)Adaptive实例的代码是由createAdaptiveExtensionClassCode()方法动态产生的。
2)”直到扩展点方法执行时才决定调用是哪一个扩展点实现”, 这句话理解为Adaptive代码中会根据extName扩展名指定加载对应实现类。
3)URL封装的信息里可能含有从配置文件xml传递过来的key=value信息,假如没有,则extName取接口类的注解@SPI(“netty”)中指定的名字,例如这里是extName为Netty,这段逻辑代码如下图部分片段:

java.lang.reflect.Method 对象反射获取注解:
 ...
 Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
 ...
 ...
 String[] value = adaptiveAnnotation.value();
 ...

这里写图片描述

上面代码片段相关逻辑在Adaptive注解中也有如何使用,具体以代码说明为准。
这里写图片描述

下图附上protocol的代码生成Adaptive实例供参考:
extName的值就是根据上面逻辑生成的
这里写图片描述
(对于Protocol扩展的使用,URL传给Protocol扩展点,基于扩展点的Adaptive机制,根据URL的协议头,进行不同协议的服务暴露或引用)


拓展延伸

类似地,Wrapper包装类是用Wrapper.makeWrapper方法动态生成新包装类的。其它类型的注解使用也是在代码层面利用Class反射原理获取注解及其值后生成代码。

启发与收获

Dubbo的设计有很多值得借鉴学习的地方,本文希望能够让你获得启发。
1. 开闭原则的面向对象设计,对于变化点的可扩展性,利用配置式服务发现机制动态加载相关接口实现类。
2. 代码动态生成,动态编译技术。
3. 平时开源框架例如Spring,那些系统注解或者自定义注解的实现原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值