在上一篇Dubbo 基于xml文件分析主流程源码 (4)_chen_yao_kerr的博客-CSDN博客中, 我们已经初步了解了Dubbo SPI的 key - value 结构。接下来将会继续分享Dubbo SPI其他功能的使用方式,并且从源码的角度去一谈究竟。
@Activate注解
参数名 效果
String[] group() URL中的分组如果匹配则激活
String[] value() URL中如果包含该key值,则会激活
String[] before() 填写扩展点列表,表示哪些扩展点要在本扩展点之前激活
String[] after() 表示哪些扩展点需要在本扩展点之后激活
int order() 排序信息
功能演示:
首先配置一个接口文件:
Filter接口是Dubbo框架提供的
下面提供一些接口的实现类,属于不同的group
FilterA
package com.enjoy.filter.impl;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
/**
* 使用方传递了group = Constants.PROVIDER 或者Constants.CONSUMER则该Filter激活
*/
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class FilterA implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("你好,调通了Filer A实现!");
return null;
}
}
FilterB
package com.enjoy.filter.impl;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
/**
* 使用方传递了group = james 则该Filter激活
*/
@Activate(group = "james",order = 2)
public class FilterB implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("你好,调通了Filer B实现!");
return null;
}
}
Filter C:
package com.enjoy.filter.impl;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
/**
* 使用方传递了group = peter 则该Filter激活
*/
@Activate(group = "peter",order = 3)
public class FilterC implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("你好,调通了Filer C实现!");
return null;
}
}
FilterD:
package com.enjoy.filter.impl;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
/**
* 使用方传递了group = peter或james 则该Filter激活
*/
@Activate(group = {"peter","james"},order = 4)
public class FilterD implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("你好,调通了Filer D实现!");
return null;
}
}
FilterE:
package com.enjoy.filter.impl;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
/**
* 使用方传递了group = peter或james,并且url中包含test5参数,则该Filter激活
*/
@Activate(group = {"peter","james"},order = 1,value = "oooooo")
public class FilterE implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("你好,调通了Filer E实现!");
return null;
}
}
测试类:
case1:获取到 group 为 peter的实现类:
我们只获取到了FilterC 和 FilterD 实现类,而FilterE并没有获取到。因为FilterE中的@Activate内部还有value属性,我们当前的测试Case并没有获取到。因此,无法获取到FilterE
Case2:获取到group为peter的实现类,同时url上提供value进行补集
也就是说,先根据group为peter的条件获取到了FilterC和FilterD,然后再根据URL上的 value以及group值再获取到了FilterE。这样就获取到了FilterC 、 FilterD 和 FilterE
case 3:根据URL对过滤到的集合进行增、删操作
源码分析
这里提前说一下@Adaptive,这是最佳适配类的意思。也就是说ExtensionFactory是Dubbo SPI的接口,而 AdaptiveExtensionFactory 类上方打了@Adaptive注解,那么这个类就是最佳适配类了。而AdaptiveExtensionFactory内部吃哟了ExtensionFactory的list对象,这样就类似于装饰模式一样,增强了功能。
普及完以上的知识,下面开始进入debug操作
首先就是获取到 ExtensionLoader 加载类。
而这个加载类的获取是利用到了@Adaptive最佳适配类的原理。
第二次进入符合条件,也就说是获取到的 ExtensionLoader对象内部的 objectFactory 为 null
依次返回到第一次new ExtensionLoader对象的时候,会调用后面的 getAdaptiveExtension 方法。其实,它就是把这个最佳适配类AdaptiveExtensionFactory给实例化了并且当做ExtensionLoader内部的属性objectFactory。
同时,它还会去扫描配置文件中的信息,实例化bean并且放入缓存中。
上面我们演示了3个case,下面从源码的角度看这3个case:
case1分析:
List<Filter> list=extensionLoader.getActivateExtension(url,"", "peter");
调用的时候,我们发现这些缓存已经存在
接下来就是遍历这些缓存信息,逐个遍历,进行信息匹配。
其实,代码到目前为止,case1 和 case 2已经很清楚了。
1、首先匹配group,接着匹配value。value为空则只匹配group,case1结束。
2. 如果匹配到group,value存在。接着就是URL中的value和Active对象中的value值进行匹配。配到了就加入补集,没匹配到就不加入。 简单点说,就是以@Active中是否有value属性值为准,url中的只是个参考。@Active中有value,我参会检查url,如果没有,则直接忽略了url中的value。
3. 还剩下个case3的实现类增、删功能没有分析。
其实,新增、删除是分开处理的。先看新增的部分,新增是在处理完case1和case2的代码以后进行的
那么删除又是怎么操作的呢?其实这是通用逻辑,最简单:
这个注解,其实还是蛮简单的,稍微看一下代码都可以理清楚。