Dubbo源码学习

Posted lijianming180

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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源码学习的主要内容,如果未能解决你的问题,请参考以下文章

深入Dubbo源码 - Dubbo配置文件解析

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

Dubbo学习-源码学习

Dubbo源码研读记

DUBBO源码学习

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