目录
一、简介:
二、用法
三、源码
四、小结:
@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