dubbo扩展的实现原理11

1       概要介绍及使用方法

1.1    Java SPI

关于 java spi 的介绍能够参见下面这个帖子java

http://singleant.iteye.com/blog/1497259缓存

 

Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制增强而来。ruby

Dubbo改进了JDK标准的SPI的如下问题:app

  • JDK标准的SPI会一次性实例化扩展点全部实现,若是有扩展实现初始化很耗时,但若是没用上也加载,会很浪费资源
  • 若是扩展点加载失败,连扩展点的名称都拿不到了。好比:JDK标准的ScriptEngine,经过getName();获取脚本类型的名称, 但若是RubyScriptEngine由于所依赖的jruby.jar不存在,致使RubyScriptEngine类加载失败,这个失败缘由被吃掉 了,和ruby对应不起来,当用户执行ruby脚本时,会报不支持ruby,而不是真正失败的缘由
  • 增长了对扩展点IoCAOP的支持,一个扩展点能够直接setter注入其它扩展点

 

1.2    扩展使用方法:

在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。ide

示例:函数

在协议的实现jar包内放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,内容为:url

xxx=com.alibaba.xxx.XxxProtocolspa

实现类内容:代理

package com.alibaba.xxx;

import com.alibaba.dubbo.rpc.Protocol;

public class XxxProtocol implemenets Protocol {

    // ...

}

 

加载扩展实现, dubbo中都是经过ExtensionLoader实现的, 代码如:code

ExtensionLoader.getExtensionLoader(Container.class).getExtension("registry");

2       ExtensionLoader代码分析

咱们以方法getExtensionLoader做为入口来分析加载的实现:

 public static<T> ExtensionLoader<T> getExtensionLoader(Class<T> type){

    if(type==null)

        throw new IllegalArgumentException("Extensiontype == null");

    if(!type.isInterface()){

        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");

    }

    if(!withExtensionAnnotation(type)){

        thrownewIllegalArgumentException("Extensiontype("+type+

   ") is not extension, because WITHOUT@" + SPI.class.getSimpleName() + " Annotation!");

    }



    ExtensionLoader<T> loader=(ExtensionLoader<T>)EXTENSION_LOADERS.get(type);

    if(loader==null){

        EXTENSION_LOADERS.putIfAbsent(type,newExtensionLoader<T>(type));

        loader=(ExtensionLoader<T>)EXTENSION_LOADERS.get(type);

    }

    return loader;

}

 

 

 

此方法根据扩展类型, 获得一个扩展加载器(ExtensionLoader)

1, 经过方法开始部分的几个校验能够知道:

·         扩展类泛型类型不能为空

·         必须是一个接口.

·         此接口必须打上@SPI注解

2, 先从EXTENSION_LOADERS缓存里取, 取不到会实例化一个ExtensionLoader, 而且缓存起来;

ExtensionLoader的构造器中, 会经过AdaptiveExtension方式获得一个ExtensionFactory; AdaptiveExtensionExtensionFactory后面会再讲到.

3,获得ExtensionLoader以后, 再来看看getExtension(String name)方法

逻辑比较简单,

·         若是name 传一个 "true", 表示取默认扩展; getDefaultExtension(), 实现逻辑见4.

·         先从缓存cachedInstances里取, 取不到经过createExtension方法建立, 完了再放缓存.

 

4, 获取默认扩展的实现逻辑.getDefaultExtension

5, createExtension方法:

 1) 经过getExtensionClasses方法, 取得全部的扩展类缓存, 并从中取得name对应的class. getExtensionClasses实现逻辑见6.

·         根据类型, EXTENSION_INSTANCES得到缓存的实例.

·         若是不存在, 则经过class.newInstance建立实例, 并缓存.

·         经过injectExtension方法, 给这个实例注入各类属性.injectExtension实现逻辑, 详见7.

·         若是待扩展的T类型,Wrapper包装器类(构造函数有T入参),实例化,而且injectExtension:

Set<Class<?>> wrapperClasses = cachedWrapperClasses;

    if(wrapperClasses != null && wrapperClasses.size() > 0){

        for(Class<?> wrapperClass : wrapperClasses){

        instance = injectExtension((T)wrapperClass.getConstructor(type).newInstance(instance));

    }

}

return instance;

 

能够到看最后返回的是最后一个包装器类;以前的包装器类以及最开始最纯粹的扩展类实例, 只是调用了一下构造器方法, 而后传递给后面的包装器类; 这样就能在各级包装器类中添加各类扩展属性级扩展方法(装饰器模式).

 

包装类在loadFile加载扩展类的时候, 加载到cachedWrapperClasses缓存的.(loadFile实现逻辑详见8)

6.getExtensionClasses 获取全部的扩展类, 实现逻辑

    1) cachedClasses缓存里取, 若是不存在, 经过loadExtensionClasses方法加载.

    2) loadExtensionClasses实现逻辑:

        a.经过getExtensionLoader(Class<T>type)初始传进来的type类型上的SPI注解的value, 解析获得一个cachedDefaultName(这个就是默认扩展了.

        b.经过loadFile方法(实现逻辑见8), 加载DUBBO_INTERNAL_DIRECTORY,DUBBO_DIRECTORY,SERVICES_DIRECTORY三个目录下的扩展类汇总到extensionClasses缓存, 最后一块儿返回.

7.injectExtension方法,给实例注入属性的实现逻辑.

经过反射, 找到只有一个入参的set public方法, 获得参数类型.

经过ExtensionFactory获得这个属性的扩展实例, 若是存在的话, 就注入.

8, loadFile 经过配置文件加载扩展类的实现逻辑:

1)得到指定扩展配置文件名, :META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler.Compiler

2)经过类加载器获得配置文件资源Url, 在类路径下可能会找到多个.

3)遍历, 读取配置文件中的每一行, 并解析, 配置行形如:javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

4)经过class.forName将等号后面的类, 转为类类型.

5)校验:

a.必须是T接口的实现类 type.isAssignableFrom(clazz)

b. 若是这个类有@Adaptive注解. 而且缓存到cachedAdaptiveClass, 若是cachedAdaptiveClass已经有了, 但不是当前解析获得的class, 说明该接口有多个@Adaptive注解扩展类, 报错.关于@Adaptive后面会再分析.

c.若是不是@Adaptive扩展类, 尝试得到该类带有该类类型为入参的构造函数

clazz.getConstructor(type);

存在, 则认为是包装器类, 加入到包装器缓存.

d.若是不是包装器类,

若是配置行, 仅有类名, 没有=号及等号左边的内容, 看看这个类上有没有@Extension注解, 而且注解的值与类名

若是仍是没有若是扩展类的类名是接口类类名结尾.

clazz.getSimpleName().length()>type.getSimpleName().length()

&&clazz.getSimpleName().endsWith(type.getSimpleName()

那么 扩展名就是去掉接口名以后, 前半部分.

不然报错.

这个扩展名, 容许逗号隔开, 配置多

从扩展类上获取@Activate注解, 若是有, 存入cachedActivates缓存, 若是扩展名有多个, 只以第一个做为缓存key.

遍历每个扩展名, 放入缓存, 名称扩展类对应扩展名.同时放入extensionClasses缓存,扩展名对应扩展类.

 

 

ExtensionLoader 还可加载Activate getActivateExtension()

 

Adaptive实例,直到扩展点方法执行时才决定调用是一个扩展点实现。

 

扩展点方法调用会有URL参数(或是参数有URL成员)

这样依赖的扩展点也能够从URL拿到配置信息,全部的扩展点本身定好配置的Key后,配置信息从URL上从最外层传入。

 

Adaptive实例的逻辑是固定,指定提取的URLKey,便可以代理真正的实现类上,能够动态生成。

 

DubboExtensionLoader的扩展点类开对应的Adaptive实现是在加载扩展点里动态生成。指定提取的URLKey经过@Adaptive注解在接口方法上提供。

下面是DubboTransporter扩展点的代码:

public interface Transporter {

    @Adaptive({"server", "transport"})

    Server bind(URL url, ChannelHandler handler) throws RemotingException;


    @Adaptive({"client", "transport"})

    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

 

对于bind方法表示,Adaptive实现先查找"server"key,若是该Key没有值则找"transport"key值,来决定代理到哪一个实际扩展点。

 

调用扩展以下面的代码:

Protocol refprotocol= ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

 

下面是getAdaptiveExtension()方法的实现逻辑:

 

1, cachedAdaptiveInstance缓存获取Adaptive类实例, 若是不存在, 调用createAdaptiveExtension()方法建立, 并缓存

2, createAdaptiveExtension方法, 经过getAdaptiveExtensionClass()获得Adaptive类并实例化, 而且经过injectExtension方法注入各属性.

 

3, getAdaptiveExtensionClass()实现逻辑: 从缓存cachedAdaptiveClass中获取, 取不到调用createAdaptiveExtensionClass方法建立

 

4, 经过createAdaptiveExtensionClassCode方法生成@Adaptive类字节码, 而且经过Compiler类编译获得, Compiler也是经过ExtensionLoader getAdaptiveExtension获得的.

这里不会递归调用栈溢出吗?

因为在getExtensionClasses()中已经找了cachedAdaptiveClass(8.5.b), 因此若是cachedAdaptiveClass仍然为空, 会去createAdaptiveExtensionClass() , 这样就会调用堆栈溢出. 这样就不难理解为啥8.5.b, 若是找到多个@Adaptive注解的类会报异常.

 

Compiler有一个实现类加了@Adaptive注解, 因此不会再去createAdaptiveExtensionClass.

 

AdaptiveCompiler

 

@Adaptive

public class AdaptiveCompilerimplementsCompiler

相关文章
相关标签/搜索