Dubbo源码 之 Activate源码分析

    技术2024-12-24  13

    目录

                      一、简介:

    二、用法

    三、源码

    四、小结:


    一、简介:

    @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

    Processed: 0.011, SQL: 9