Dubbo 源码分析 - Dubbo SPI 获取自适应扩展点实例

Dubbo SPI 系列文章

前言

上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 分析了 Dubbo SPI 通过 getExtension(String name)如何获取扩展点实现类对象, 本文接着通过剖析 Dubbo 源码的方式看一下 getAdaptiveExtension()方法是如何获取自适应扩展点实例的.

正文

1. ExtensionLoader 部分属性

 // 1. 缓存的自适应扩展点实例
 private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
 // 2. 自适应扩展点实现类
 private volatile Class<?> cachedAdaptiveClass = null;

2. ExtensionLoader#getAdaptiveExtension()

  1. 从自适应扩展点实例 缓存集合中获取, 赋值给 instance变量
  2. 如果缓存中没有,就通过 createAdaptiveExtension方法主动去创建, 并放入缓存
  3. 返回结果 return instance
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 第一步
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }

2. ExtensionLoader#createAdaptiveExtension()

  • 先调用 getAdaptiveExtensionClass方法, 获取自适应扩展实现类, 并且实例化,最终获得一个实例 instance
  • 再通过injectExtension方法, 判断 instance是否依赖其他扩展实现类对象, 如果有的话,通过 setter方法注入到instance实例中

    private T createAdaptiveExtension() {
        try {
            // 第二步: getAdaptiveExtensionClass
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
2.1 getAdaptiveExtensionClass
  • getExtensionClasses方法在上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 有讲到,
    • 先判断缓存的扩展实现类 cachedClasses是否为空
    • 如果为空,就去META-INF 目录下,解析对应子目录下的 SPI 配置文件, 最后放入缓存cachedClasses
  • 接着判断缓存的自适应扩展实现类是否为空
  • 如果为空, 就进入第三步: 动态编译
   
     private Class<?> getAdaptiveExtensionClass() {
     
        // 这一步会 load 所有的扩展实现类
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 第三步: 动态编译
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
2.1.2 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);
    }
   
    // 动态生成 Adaptive 代码
    package com.nimo.spi.adaptive;
    import org.apache.dubbo.common.extension.ExtensionLoader;
    public class MyProtocol$Adaptive implements com.nimo.spi.adaptive.MyProtocol {
        public void export(org.apache.dubbo.common.URL arg0)  {
            if (arg0 == null) throw new IllegalArgumentException("url == null");
                org.apache.dubbo.common.URL url = arg0;
                String extName = url.getParameter("myKey");
            if(extName == null) 
                throw new IllegalStateException("Failed to get extension     (com.nimo.spi.adaptive.MyProtocol) name from url (" + url.toString() + ") use keys([myKey])");
            
            com.nimo.spi.adaptive.MyProtocol extension =     (com.nimo.spi.adaptive.MyProtocol)ExtensionLoader.getExtensionLoader(com.nimo.spi.adaptive.MyProtocol.class).getExtension(extName);
            extension.export(arg0);
        }
    }

2.2 injectExtension

该方法就是对获取到的自适应扩展点实例, 注入其他实例

   // 第四步: 注入其他实例
   private T injectExtension(T instance) {

       if (objectFactory == null) {
           return instance;
       }

       try {
           for (Method method : instance.getClass().getMethods()) {
               if (!isSetter(method)) {
                   continue;
               }
               /**
                * Check {@link DisableInject} to see if we need auto injection for this property
                */
               if (method.getAnnotation(DisableInject.class) != null) {
                   continue;
               }
               Class<?> pt = method.getParameterTypes()[0];
               if (ReflectUtils.isPrimitives(pt)) {
                   continue;
               }

               try {
                   String property = getSetterProperty(method);
                   Object object = objectFactory.getExtension(pt, property);
                   if (object != null) {
                       method.invoke(instance, object);
                   }
               } catch (Exception e) {
                   logger.error("Failed to inject via method " + method.getName()
                           + " of interface " + type.getName() + ": " + e.getMessage(), e);
               }

           }
       } catch (Exception e) {
           logger.error(e.getMessage(), e);
       }
       return instance;
   }
  

总结

Dubbo SPI 中定义了很多缓存, 比如上一篇提到的

     // 3. (缓存的)扩展实现类集合
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

    // 4. 缓存的扩展对象集合 key 为 dubbo, value 为 DubboProtocol
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
 
    // 5. 扩展增强 Wrapper 实现类集合
    private Set<Class<?>> cachedWrapperClasses;

还有本文中涉及的

 // 1. 缓存的自适应扩展点实例
 private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
 // 2. 自适应扩展点实现类
 private volatile Class<?> cachedAdaptiveClass = null;

因此在获取一个扩展点实例时,

  • 都会先去缓存中获取,
  • 如果获取不到就去重新加载并实例化,
  • 最后把实例放入缓存,

这是一个很普遍的缓存思路, 上一篇文章 Dubbo 源码分析 - Dubbo SPI 获取扩展点实现类对象 getExtension方法也是如此, 只不过本文多了一个 动态编译过程, 也刚好迎合了前面讲到的 Dubbo SPI 使用方法(二)- 扩展点自适应 的内容.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值