Dubbo源码学习(二)

@Adaptive注解

在上一篇ExtensionLoader的博客中记录了,有两种扩展点,一种是普通的扩展实现,另一种就是自适应的扩展点,即@Adaptive注解的实现类。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

@Adaptive注解可以加载实现类的类上或方法上。下面可以通过源码去分析,先放结论:

  • 注解在实现类上时,提供一种标识表名此实现类并不提供具体的业务实现,而是一个该接口的适配器。例如AdaptiveCompiler、AdaptiveExtensionFactory。
  • 注解在接口方法上时,根据某种规则动态生成代码,动态适配。例如Protocol接口

主要方法

T getAdaptiveExtension()

调用流程

T getAdaptiveExtension()         //在cachedAdaptiveInstance缓存中获取,获取不到则进入下一步创建
    -> T createAdaptiveExtension()
        -> getAdaptiveExtensionClass()      //加载完配置文件后,会在cachedAdaptiveClass缓存中获取,获取不到则创建
            -> getExtensionClasses()          //如果第一次,则加载配置文件
            -> createAdaptiveExtensionClass()
                -> createAdaptiveExtensionClassCode()   //动态生成代码
                -> Compiler.compile(String code, ClassLoader classLoader)   //编译动态生成的代码
        -> injectExtension(T instance)          //注入

cachedAdaptiveClass缓存是@Adaptive注解在类上的缓存,所以上述代码的大致就是:

  • 如果@Adaptive注解在类上,则直接返回这个类的实例
  • 如果@Adaptive注解在方法上,则动态生成一个类。

createAdaptiveExtensionClassCode()

动态生成的代码比较长,但是主要的逻辑就是通过Adaptive注解的value属性去设置该接口的Adaptive的规则,
Dubbo中的服务调用的所有数据均可以从URL获取(Dubbo的URL总线模式),那么需要Dubbo帮我们动态生成adaptive的扩展接口的方法入参必须包含URL,这样才能根据运行状态动态选择具体实现。
一个简单的生成类如下代码:

@Adaptive({"key1", "key2"})
String yell(URL url, String s);
public String yell(URL arg0, String arg1) {
    if (arg0 == null) {
        throw new IllegalArgumentException("url == null");
    }
    URL url = arg0;
    String extName = url.getParameter("key1", url.getParameter("key2", "impl1"));
    if(extName == null) {
        throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([key1, key2])");
    }
    SimpleExt extension = (SimpleExt)ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension(extName);
    return extension.yell(arg0, arg1);
}

@Ac 大专栏  Dubbo源码学习(二)tivate注解

可以看到ExtesionLoader中还有一些与@Active注解相关的方法。

  • List getActivateExtension(URL url, String key)
  • List getActivateExtension(URL url, String key, String group)
  • List getActivateExtension(URL url, String[] values, String group)

可以看出,这些方法都是根据url的某些条件获取一个实现类的列表。@Activate注解主要用处是标注在插件接口实现类上,用来配置该扩展实现类激活条件。
在Dubbo框架里面的Filter的各种实现类都通过Activate标注,用来描述该Filter什么时候生效。下面是一些例子:

  • MonitorFilter( @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}) ) 用来告诉Dubbo框架这个Filter是在服务提供端和消费端会生效的;
  • TimeoutFilter( @Activate(group = Constants.PROVIDER) )则是只在服务提供端生效,消费端是不会调用该Filter;
  • ValidationFilte( @Activate(group = { Constants.CONSUMER, Constants.PROVIDER }, value = Constants.VALIDATION_KEY, order = 10000) )
    要激活的条件除了在消费端和服务提供端激活,value 表明了URL参数中必须包含validation才会被激活。order 即激活顺序。

getActivateExtension的实现

代码很简单,就做了下简单的注释。

public List<T> getActivateExtension(URL url, String[] values, String group) {
    List<T> exts = new ArrayList<T>();
    List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
    if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {  //"-default"
        getExtensionClasses();      //未加载配置文件的加载一遍
        for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
            String name = entry.getKey();
            Activate activate = entry.getValue();
            if (isMatchGroup(group, activate.group())) {    //如果符合指定的group
                T ext = getExtension(name);
                if (! names.contains(name)
                        && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)
                        && isActive(activate, url)) {
                    exts.add(ext);
                }
            }
        }
        Collections.sort(exts, ActivateComparator.COMPARATOR);      //排序
    }
    List<T> usrs = new ArrayList<T>();
    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;
}


Dubbo源码学习(二)

原文地址:https://www.cnblogs.com/lijianming180/p/12239807.html

时间: 2024-10-06 23:25:01

Dubbo源码学习(二)的相关文章

dubbo源码学习(二) : spring 自定义标签

做dubbo的配置时很容易发现,dubbo有一套自己的标签,提供给开发者配置,其实每一个标签对应着一个 实体,在容器启动的时候,dubbo会对所有的配置进行解析然后将解析后的内容设置到实体里,最终dubbo会根据实体中的值生成贯穿全局的统一URL.利用自定义标签使配置简单明了化,与spring完美融合.下面自己写一个自定义标签,主要需要如下 几个步骤: 1.编写实体类2.编写Parser解析类3.编写NameSpaceHandle类4.配置spring.handlers5.配置spring.sc

Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题,同时最近又看了一下 Dubbo 的源码,想重新写一下 Dubbo 相关的文章. 优雅停机原理 对于一个 java 应用,如果想在关闭应用时,执行一些释放资源的操作一般是通过注册一个 ShutDownHook ,当关闭应用时,不是调用 kill -9 命令来直接终止应用,而是通过调用 kill -15 命令来触发这个 ShutDownHook 进行停机前的释放资源操作.

Dubbo源码学习之-Adaptive自适应扩展

前言 最近三周基本处于9-10-6与9-10-7之间,忙碌的节奏机会丢失了自己.除了之前干施工的那段经历,只看参加软件开发以来,前段时间是最繁忙的了.忙的原因,不是要完成的工作量大,而是各种环境问题,各种沟通协调问题.从这个项目,我是体会到了人一多,花在沟通协调上的成本真的会不成比例的放大,制度好,再加上协调好,会极大的提高整体工作效率.怪不得当年华为跟IBM学完工作组织管理制度之后能爆发出如此强劲的战斗力.从另一个角度,也能发觉出为什么大公司招人都比较注重员工的个人实力与团队协作能力,因为如果

dubbo源码学习(四)初始化过程细节:解析服务

初学dubbo的源码,只做尝试性的去学习,做为自己学习的一个记录,各位看官如果觉得写的有错误或理解的不对,请在留言区告诉我,互相学习.本人能力有限,有大神进入 时请指点. 前面大概介绍了一下关于学习dubbo源码的一些基本知识,今天将真正去看dubbo内部的实现过程,看dubbo的源码前我先把dubbo的用户指南和开发指指南大概的看了一遍,然后从上面找到相应的切入点去看源码,今天将介绍的是dubbo的初始化解析bean的过程.从之前使用过dubbo一些经验,加上http://dubbo.io/的

dubbo源码学习(五)dubbo暴露服务的过程

初学dubbo的源码,只做尝试性的去学习,做为自己学习的一个记录,各位看官如果觉得写的有错误或理解的不对,请在留言区告诉我,互相学习.本人能力有限,有大神进入 时请指点. dubbo采用的nio异步的通信,通信协议默认为 netty,当然也可以选择 mina,grizzy.在服务端(provider)在启动时主要是开启netty监听,在zookeeper上注册服务节点,处理消费者请求,返回处理后的消息给消费者,消费者使用服务时主要是订阅服务的节点,监听zookeeper节点目录,服务端的变化时z

python 协程库gevent学习--gevent源码学习(二)

在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用monkey_patchall()之后隐式切换的问题. 下面我将继续通过分析源码及其行为来加以理解和掌握. 1. 关于gevent.Greenlet.join()(以下简称join)先来看一个例子: import gevent def xixihaha(msg): print(msg) gevent.sl

Dubbo源码学习之-服务导出

前言 忙的时候,会埋怨学习的时间太少,缺少个人的空间,于是会争分夺秒的工作.学习.而一旦繁忙的时候过去,有时间了之后,整个人又会不自觉的陷入一种懒散的状态中,时间也显得不那么重要了,随便就可以浪费掉几个小时.可见普通人的学习之路要主动地去克服掉很多阻碍,最主要的阻碍还是来自于自身,周期性的不想学习.不自觉的懒散.浅尝辄止的态度.好高骛远贪多的盲目...哎,学习之路,还是要时刻提醒自己,需勤勉致知. 闲话少叙,今天的学习目标是要尽量的了解清楚Dubbo框架中的服务导出功能,先附上Dubbo官网上的

[spring源码学习]二、IOC源码——配置文件读取

一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void sayHello

dubbo源码学习(一)基础知识及使用的相关技术

初学dubbo的源码,只做尝试性的去学习,做为自己学习的一个记录,各位看官如果觉得写的有错误或理解的不对,请在留言区告诉我,互相学习.本人能力有限,有大神进入 时请指点. Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合),我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就