彩色科技|Dubbo自适应扩展机制( 二 )

  • 扩展点自动装配
  • 扩展点自适应
  • 扩展点自动激活
  • 在介绍各个特性前先介绍下大概的内部实现 。 从上面的使用中可以看到Dubbo对SPI扩展的主要实现在ExtensionLoader类中 , 关于这个类源码的讲解可以看官方文档 , 讲解的很详细 , 这边主要说下大概过程:
    1. 根据传入的类型从classpath中查找META-INF/dubbo/internal和META-INF/dubbo路径下所有对应的扩展点配置文件
    2. 读取扩展点配置文件中所有的键值对
    3. 根据键值对缓存Class对象 , 如果类有同类型参数的构造函数 , 则为包装类 , 会缓存在另一个容器中
    4. 实例化对象 , 缓存后并返回
    2.1 扩展点自动包装在返回真正实例前 , 会用该类型的包装类进行包装 , 既采用装饰器模式进行功能增强 。
    if (CollectionUtils.isNotEmpty(wrapperClasses)) {// 循环创建 Wrapper 实例for (Class wrapperClass : wrapperClasses) {// 将当前 instance 作为参数传给 Wrapper 的构造方法 , 并通过反射创建 Wrapper 实例 。// 然后向 Wrapper 实例中注入依赖 , 最后将 Wrapper 实例再次赋值给 instance 变量instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));}}其实就是使用静态代理来实现AOP
    彩色科技|Dubbo自适应扩展机制2.2 扩展点自动装配返回实例前会遍历所有的setXXX方法 , 判断set方法参数是否存在自适应对象 , 如果存在则通过ExtensionLoader加载自适应对象然后进行赋值 , 可以通过方法上加org.apache.dubbo.common.extension.DisableInject注解来屏蔽该功能 。 该功能其实就是实现了IOC , 具体可以看ExtensionLoader的injectExtension方法 。
    2.3 扩展点自适应同上面介绍的一样 , Dubbo的SPI可以根据传入的URL参数中携带的数据来动态选择具体的实现 。
    2.4 扩展点自动激活上面介绍过 , Adaptive注解是加在方法上的 , 类似的有个注解org.apache.dubbo.common.extension.Activate是加在实现类上 。 当加在Class上时 , ExtensionLoader会将对应的key和Class信息缓存到另一个容器中 , 后续可以通过ExtensionLoader获取某一类的实现列表 , 既如下方法
    public List getActivateExtension(URL url, String[] values){...}3. ExtensionLoader自适应扩展机制ExtensionLoader自适应扩展机制的大概实现逻辑是这样的:Dubbo会为拓展接口生成具有代理功能的代码 , 然后通过 javassist 或 jdk 编译这段代码 , 得到 Class 类 。 最后再通过反射创建代理类 , 在代理类中 , 就可以通过URL对象的参数来确定到底调用哪个实现类 。 主要实现在createAdaptiveExtensionClass方法中 。
    private Class createAdaptiveExtensionClass() {String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();ClassLoader classLoader = findClassLoader();org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();return compiler.compile(code, classLoader);}上面的Protocol接口经过处理后的内容如下:
    public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {public void destroy() {throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort() {throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");}if (arg0.getUrl() == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");}org.apache.dubbo.common.URL url = arg0.getUrl();String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null) {throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");}org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {if (arg1 == null) {throw new IllegalArgumentException("url == null");}org.apache.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null) {throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");}org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}}