目录
一、简介:
@Activate称为自动激活扩展点注解,主要使用在有多个扩展点实现、需要同时根据不同条件被激活的场景中
二、用法
这里列举了@Activate注解不同属性的作用的几种场景,我们首先来看下使用效果。
前期准备:
新建一个接口和五个实现类
@Activate(value = {"activateKey2"}, group = {"order"})
public class ValueActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am ValueActivateImpl.");
}
}
@Activate(order = 2, group = {"order"})
public class Order2ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order2ActivateImpl.");
}
}
@Activate(order = 1, group = {"order"})
public class Order1ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order1ActivateImpl.");
}
}
@Activate(group = {"order"})
//@Adaptive
public class OptimusPrime implements Robot {
@Override
public void sayHello(String url) {
System.out.println("Hello, I am OptimusPrime");
}
}
@Activate(after = "valueActivateImpl",group = "order")
public class BeforeActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am BeforeActivateImpl.");
}
}
测试main方法
①、@Activate#group
group表示分组如果匹配则激活,目前几个实现类都是group都是order,直接运行结果都被执行,和group匹配的都被执行。
②、@Activate#value
value表示URL有该key值,则激活,在ValueActivateImpl@Activate注解上加上value属性,和url中某个key保持一致。
在Order2ActivateImpl@Activate注解上同样加上value属性,但是和ValueActivateImpl区分开
其他的实现类group我改成其他的了,为了测试结果容易区分
value值和url中的key可以匹配上的才能被执行。
③、@Activate#before after
before表示哪些扩展点要在本扩展点之前激活,也就是当前扩展类是谁的before
after哪些扩展点要在本扩展点之后激活,也就是当前扩展类是谁的after
将代码还原原始状态,先测试执行不加before和after的
@Activate(group = "order")
public class BeforeActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am BeforeActivateImpl.");
}
}
@Activate(group = {"order"})
//@Adaptive
public class OptimusPrime implements Robot {
@Override
public void sayHello(String url) {
System.out.println("Hello, I am OptimusPrime");
}
}
@Activate(order = 1, group = {"order"})
public class Order1ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order1ActivateImpl.");
}
}
@Activate(order = 2, group = {"order"})
public class Order2ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order2ActivateImpl.");
}
}
@Activate(value = {"activateKey2"}, group = {"order"})
public class ValueActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am ValueActivateImpl.");
}
}
执行得到当前的一种执行时机顺序,然后我们加上before和after看是否能够根据我们的意愿进行执行
其实我就是想把BeforeActivateImpl和OptimusPrime的顺序、Order1和Order2的顺序颠倒。看下效果。符合我们的意愿。
④、@Activate#order
order表示同一组里面的排序,序号越小优先级越高
借助于上面的代码,我把order1和order2的序号改变下
@Activate(order = 2, group = {"order"})
public class Order1ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order1ActivateImpl.");
}
}
@Activate(order = 1, group = {"order"})
public class Order2ActivateImpl implements Robot{
@Override
public void sayHello(String url) {
System.out.println("Hello, I am Order2ActivateImpl.");
}
}
正常情况下order2应该优先于order1的执行顺序。
三、源码
现在我们从源码的角度分析@Activate的执行原理和各个属性的执行优先级顺序是怎么决定的。
从main方法作为入口我们首先看getExtensionLoader方法
这个方法总的目的就是获取ExtensionLoader,接下来 是其执行步骤:①、校验type如果为空,抛异常、type如果非接口、抛异常
、如果type没有扩展的注解修饰、抛异常。②、从缓存map中取,如果为空,生成ExtensionLoader放入map当中。③,如果缓存中能够取到,直接返回。
然后进入extExtensionLoader.getActivateExtension获取扩展的方法。
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// TODO 加载配置路径下的接口的实现类。
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {
T ext = getExtension(name);
if (!names.contains(name) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) && isActive(activate, url)) {
exts.add(ext);
}
}
}
// TODO 对exts这个list进行排序,排序规则是自己定义的一种优先级顺序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
if (usrs.size() > 0) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (usrs.size() > 0) {
exts.addAll(usrs);
}
return exts;
}
exts用来存放最后返回的扩展类列表。names用来存放我们传进来的values,这里我测试的时候values是没有放值的。
然后看getExtensionClasses方法,用来加载配置路径下的接口的实现类
//getExtensionClasses加载扩展点实现类
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
主要流程就是从cachedClasses中取classes,DCL检查如果最终还是为空就loadExtensionClasses方法返回值,放进cachedClasses缓存中。进入到cachedClasses方法。
注释写的挺清楚的,应该不用多说。经过这两个方法的调用,主要是进行一些属性变量的额初始化赋值操作,以供使用。
然后回到getActivateExtension方法。我们看到完成了一个排序操作,Collections.sort(exts, ActivateComparator.COMPARATOR);对exts这个list进行排序,排序规则是自己定义的一种优先级顺序,重点关注下ActivateComparator.COMPARATOR,进入到ActivateComparator.COMPARATOR
很明显这是dubbo自己实现了一个比较器,进行两个对象之间的排序规则设定。在排序规则compare中我们很容易看出主要进行了@Activate注解before和after属性、order属性的元素顺序判定。
public int compare(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.equals(o2)) {
return 0;
}
Activate a1 = o1.getClass().getAnnotation(Activate.class);
Activate a2 = o2.getClass().getAnnotation(Activate.class);
if ((a1.before().length > 0 || a1.after().length > 0
|| a2.before().length > 0 || a2.after().length > 0)
&& o1.getClass().getInterfaces().length > 0
&& o1.getClass().getInterfaces()[0].isAnnotationPresent(SPI.class)) {
ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(o1.getClass().getInterfaces()[0]);
if (a1.before().length > 0 || a1.after().length > 0) {
String n2 = extensionLoader.getExtensionName(o2.getClass());
for (String before : a1.before()) {
//o1 在 o2 之前
if (before.equals(n2)) {
return -1;
}
}
for (String after : a1.after()) {
//o2 在 o1 之前
if (after.equals(n2)) {
return 1;
}
}
}
if (a2.before().length > 0 || a2.after().length > 0) {
String n1 = extensionLoader.getExtensionName(o1.getClass());
for (String before : a2.before()) {
if (before.equals(n1)) {
//o2 在 o1 之前
return 1;
}
}
for (String after : a2.after()) {
if (after.equals(n1)) {
//o1 在 o2 之前
return -1;
}
}
}
}
int n1 = a1 == null ? 0 : a1.order();
int n2 = a2 == null ? 0 : a2.order();
// 即使n1等于n2,也永远不会返回0,否则,o1和o2将像HashSet这样在集合中相互覆盖
return n1 > n2 ? 1 : -1;
}
然后回到getActivateExtension方法。接下来就是对额外的额指定的names进行获取扩展类放入exts中,我这里么有放值,暂时不做研究。
四、小结:
@Activate注解各个属性声明的值是通过源码中sort方法进行排序的,排序规则是dubbo自己实现的比较规则。除了before、after、order其他的排序规则由jdk实现(默认升序)。
在value、before、after、order值都指定了的情况下首先是jdk实现(默认升序)的扩展类,然后是before、after指定的扩展类,然后是order指定的顺序。
个人才疏学浅、信手涂鸦,dubbo框架更多模块解读相关源码持续更新中,感兴趣的朋友请移步至个人公众号,谢谢支持😜😜......
公众号:wenyixicodedog