扩展点注解:@SPI
@SPI注解可以使用在类、接口和枚举类
上,Dubbo框架中都是使用在接口上。它的主要作用就是标记这个几口是一个Dubbo SPI接口,即是一个扩展点,可以有多个不同的内置或用户定义的实现。
运行时需要通过配置找到具体的实现类。
可以看到SPI注解有一个value属性,通过这个属性,我们可以传入不同的参数来设置这个接口额默认实现类。例如,我们可以看到Transporter接口使用Netty作为默认实现:
Dubbo中很多地方通过getExtension(Class<T> type, String name)
来获取扩展点接口的具体实现,此时会传入Class做校验,判断是否是接口,以及是否有@SPI注解,两者缺一不可。
扩展点自适应注解:@Adaptive
@Adaptive注解可以标记在类、接口、枚举类和方法
上,但是在整个Dubbo框架中,只有几个地方使用在类级别上,如AdaptiveExtensionFactory和AdaptiveCompiler
,其余都标注在方法上。
如果标注在接口的方法上,即方法级别注解,则可以通过参数动态获得实现类。方法级别注解在第一次getExtension时,会自动生成和编译一个动态的Adaptive类,从而达到动态实现类的效果。
例如:Transporter接口在bind和connect两个方法上添加了@Adaptive注解,如下所示:
Dubbo在初始化扩展点时,会生成一个Transporter#Adaptive类
,里面会实现这两个方法,方法里会有一些抽象的通用逻辑,通过@Adaptive中传入的参数,找到并调用真正的实现类。自动生成的代码中实现了很多通用的功能,最终会调用真正的接口实现。
当该注解放在实现类上,则整个实现类会直接作为默认实现,不再自动生成代码清单。在扩展点接口的多个实现里,只能有一个实现上可以加@Adaptive注解
。如果多个实现类中都有该注解,则会抛出异常:More than 1 adaptive class found
。
该注解也可以传入value参数,是一个数组。Adaptive可以传入多个key值,在初始化Adaptive注解的接口时,会先对传入的URL进行key值匹配,第一个key没匹配上则匹配第二个,以此类推。直到所有的key匹配完毕,如果还没有匹配到,则会使用"驼峰规则"匹配,如果也没有匹配到,则会抛出IllegalStateException异常。
驼峰规则:
如果包装类(Wrapper)没有用Adaptive指定key值,则Dubbo会自动把接口名称根据驼峰大小写分开,并用"."
符号连接起来,以此来作为默认实现类的名称,如org.apache.dubbo.xxx.YyyInvokerWrapper
中的YyyInvokerWrapper
会被转换为yyy.invoker.warpper
。
为什么有些实现类上会标注@Adaptive?
放在实现类上,主要是为了直接固定对应的实现而不需要动态生成代码实现,就像策略模式直接确定实现类。
在代码中的实现方式是:ExtensionLoader中会缓存两个与@Adaptive有关的对象:
一个缓存在cachedAdaptiveClass中,即Adaptive具体实现类的Class类型;
另外一个缓存在cachedAdaptiveInstance中,即Class的具体实例化对象;
在扩展点初始化时,如果发现实现类有@Adaptive注解,则直接赋值给cachedAdaptiveClass,后续实例化类的时候,就不会再动态生成代码,直接实例化cachedAdaptiveClass,并把实例缓存到cachedAdativeInstance中。
如果注解在接口方法上,则会根据参数,动态获得扩展点的实现,会生成Adaptive类,再缓存到cachedAdaptiveInstance中。
扩展点自动激活注解:@Activate
@Activate可以标记在类、接口、枚举类和方法上
。主要使用在有多个扩展点实现、需要根据不同条件被激活的场景中,如Filter需要多个同时激活,因为每个Filter实现的是不同的功能。
@Activate可传入的参数:
参数名 | 效果 |
String[] group() | URL中的分组如果匹配则激活,则可以设置多个 |
String[] value() | 查找URL中如果含有该key值,则会激活 |
String[] before() | 填写扩展点列表,表示那些扩展点要在本扩展点之前 |
String[] after() | 同上,表示哪些需要在本扩展点之后 |
int order() | 整型,直接的排序信息 |