Dubbo扩展点加载机制

打印 上一主题 下一主题

主题 906|帖子 906|积分 2718

        加载机制中已经存在的一些关键注解,如@SPI、©Adaptive> ©Activateo然后先容整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后先容扩展中使用的类动态编译的实 
现原理。
Java SPI

Java 5 中的服务提供商
https://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#Service%20Provider

SPI 插件扩展点使用手册

https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/spi/
   JDK标准的SPI会一次性实例化扩展点全部实现,如果有扩展实现则初始化很耗时,如果没 
用上也加载,则浪费资源。
  如果扩展加载失败,则连扩展的名称都蕤取不到了。比如JDK标准的ScriptEngine,通过 
getName ()获取脚本类型的名称,如果RubyScriptEngine因为所依赖的j ruby .jar不存在,导致 
RubyScriptEngine类加载失败,这个失败缘故原由被“吃掉” 了,和Ruby对应不起来,当用户 
实行Ruby脚本时,会报不支持Ruby,而不是真正失败的缘故原由。
  
增加了对扩展IoC和AOP的支持,一个扩展可以直接setter注入其他扩展。在Java SPI的使 
用示例章节(代码清单4-1 )中已经看到,java.util.ServiceLoader会一次把Printservice 
接口下的全部实现类全部初始化,用户直接调用即可oDubbo SPI只是加载配置文件中的类, 
并分成不同的种类缓存在内存中,而不会立即全部初始化,在性能上有更好的表现。
   ProtocolFilterWrapper 是 Dubbo 框架中的一个核心类,用于在服务提供者和消费者之间添加过滤器链。ProtocolFilterWrapper 通过 @Activate 注解来确定哪些过滤器适用于当前的 URL。以下是 ProtocolFilterWrapper 确定过滤器适用当前 URL 的详细过程:
1. ProtocolFilterWrapper 类
ProtocolFilterWrapper 是一个装饰器模式的实现,它包装了一个 Protocol 实例,并在其上添加了过滤器链。以下是 ProtocolFilterWrapper 的主要逻辑:
  1. import com.alibaba.dubbo.common.Constants;
  2. import com.alibaba.dubbo.common.URL;
  3. import com.alibaba.dubbo.common.extension.ExtensionLoader;
  4. import com.alibaba.dubbo.rpc.Exporter;
  5. import com.alibaba.dubbo.rpc.Invoker;
  6. import com.alibaba.dubbo.rpc.Protocol;
  7. public class ProtocolFilterWrapper implements Protocol {
  8.     private final Protocol protocol;
  9.     public ProtocolFilterWrapper(Protocol protocol) {
  10.         this.protocol = protocol;
  11.     }
  12.     @Override
  13.     public int getDefaultPort() {
  14.         return protocol.getDefaultPort();
  15.     }
  16.     @Override
  17.     public Exporter<T> export(Exporter<T> exporter) {
  18.         return new InvokerDelegator<>(wrapInvoker(exporter.getInvoker()), exporter);
  19.     }
  20.     @Override
  21.     public <T> Invoker<T> refer(Class<T> type, URL url) {
  22.         return wrapInvoker(protocol.refer(type, url));
  23.     }
  24.     private <T> Invoker<T> wrapInvoker(Invoker<T> invoker) {
  25.         URL url = invoker.getUrl();
  26.         // 获取所有激活的过滤器
  27.         List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, Constants.KEYS, Constants.DEFAULT_KEY);
  28.         if (filters.size() == 0) {
  29.             return invoker;
  30.         }
  31.         return new FilterChainWrapper(invoker, filters);
  32.     }
复制代码
 SPI 提供商可以调用 ExtensionLoader.getActivateExtension(URL, String, String) 以查找具有给定条件的全部已激活的扩展。而getActivateExtension 会间接调用 com.alibaba.dubbo.common. extension.ExtensionLoader#loadExtensionClasses

其中 Type 是 由 ExtensionLoader.getExtensionLoader(Filter.class),决定为 Filter.
L565 - 567 就是剖析 Filter 的接口上@SPI注解信息.(Filter.class 也可以更换成其他的类性
com.alibaba.dubbo.common.extension.ExtensionLoader#loadDirectory 会间接调用 com.alibaba.dubbo.common.extension.ExtensionLoader#loadClass
在此方法中会剖析注解@Adaptive、@Activate
  1.     /**
  2.      * @param extensionClasses ExtensionLoader#cachedClasses 成员变量
  3.      * @param resourceURL
  4.      * @param clazz ExtensionLoader#loadResource 中 加载 Class.forName(  类全限定名 )
  5.      * @param name  ExtensionLoader#loadResource 中 在配置文件中设置的别名
  6.      *              上两参数请参考 com.alibaba.dubbo.common.extension.SPI
  7.      * @throws NoSuchMethodException
  8.      */
  9.     private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
  10.         if (!type.isAssignableFrom(clazz)) {
  11.             throw new IllegalStateException("Error when load extension class(interface: " +
  12.                     type + ", class line: " + clazz.getName() + "), class "
  13.                     + clazz.getName() + "is not subtype of interface.");
  14.         }
  15.         if (clazz.isAnnotationPresent(Adaptive.class)) {
  16.             // 由于调用者 ExtensionLoader.loadResource 循环调用了 loadClass ,如果类上标注了 @Adaptive 注解,则该类为 Adaptive 类,并且只能有一个
  17.             if (cachedAdaptiveClass == null) {
  18.                 cachedAdaptiveClass = clazz;
  19.             } else if (!cachedAdaptiveClass.equals(clazz)) {
  20.                 throw new IllegalStateException("More than 1 adaptive class found: "
  21.                         + cachedAdaptiveClass.getClass().getName()
  22.                         + ", " + clazz.getClass().getName());
  23.             }
  24.         } else if (isWrapperClass(clazz)) {
  25.             // 判断为 包装类,则维护到 ExtensionLoader.cachedWrapperClasses
  26.             Set<Class<?>> wrappers = cachedWrapperClasses;
  27.             if (wrappers == null) {
  28.                 cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
  29.                 wrappers = cachedWrapperClasses;
  30.             }
  31.             wrappers.add(clazz);
  32.         } else {
  33.             clazz.getConstructor();
  34.             if (name == null || name.length() == 0) {
  35.                 // 如果没有名字则尝试扫描 @Extension 注解, Extension 注解已经弃用了
  36.                 name = findAnnotationName(clazz);
  37.                 if (name.length() == 0) {
  38.                     throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
  39.                 }
  40.             }
  41.             // 将首个类上@Activate 信息维护到 ExtensionLoader.cachedActivates 中
  42.             // 将 别名 维护到 ExtensionLoader.cachedNames 中
  43.             // 将 别名&类 维护到 ExtensionLoader#cachedClasses 中
  44.             String[] names = NAME_SEPARATOR.split(name);
  45.             if (names != null && names.length > 0) {
  46.                 Activate activate = clazz.getAnnotation(Activate.class);
  47.                 if (activate != null) {
  48.                     cachedActivates.put(names[0], activate);
  49.                 }
  50.                 for (String n : names) {
  51.                     if (!cachedNames.containsKey(clazz)) {
  52.                         cachedNames.put(clazz, n);
  53.                     }
  54.                     Class<?> c = extensionClasses.get(n);
  55.                     if (c == null) {
  56.                         extensionClasses.put(n, clazz);
  57.                     } else if (c != clazz) {
  58.                         throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
  59.                     }
  60.                 }
  61.             }
  62.         }
  63.     }
复制代码

工作流程



  • 框架读取SPI对应路径下的配置文件,并根据配置加载全部扩展类并缓存(不初始化)。
  • 根据传入的名称初始化对应的扩展类。
  • 尝试查找符合条件的包装类:包含扩展点的setter方法,
  • 返回对应的扩展类实例。
        getAdaptiveExtension也相对独立,只有加载配置信息部分与getExtension共用了同一个 
方法。和获取普通扩展类一样,框架会先检查缓存中是否有已经初始化化好的Adaptive实例, 
没有则调用createAdaptiveExtension重新初始化。初始化过程分为4步:
和getExtension 一样先加载配置文件。

  • 天生自适应类的代码字符串。
  •  获取类加载器和编译器,并用编译器编译刚才天生的代码字符串。Dubbo 一共有三种 
  • 类型的编译器实现。
  • 返回对应的自适应类实例。
getExtension 的实现原理

ExtensionLoader#getExtension 会调用ExtensionLoader#createExtension 方法
  1.     /**
  2.      *  创建扩展实例
  3.      * @param name  别名
  4.      * @return
  5.      */
  6.     private T createExtension(String name) {
  7.         Class<?> clazz = getExtensionClasses().get(name);
  8.         if (clazz == null) {
  9.             throw findException(name);
  10.         }
  11.         try {
  12.             // 先尝试从缓存中获取实例
  13.             T instance = (T) EXTENSION_INSTANCES.get(clazz);
  14.             if (instance == null) {
  15.                 // 不存在的话则通过反射创建实例
  16.                 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
  17.                 instance = (T) EXTENSION_INSTANCES.get(clazz);
  18.             }
  19.             // 反射执行 setter 方法
  20.             injectExtension(instance);
  21.             Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  22.             if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
  23.                 // 检查是否有包装类
  24.                 for (Class<?> wrapperClass : wrapperClasses) {
  25.                     // 通过反射创建包装类实例,再执行包装实例的 setter 方法, 最后更新包装类实例
  26.                     // 这里我们能看出 包装类需要 实现 接口,并且包装类需要有一个构造函数,构造参数是接口类型
  27.                     instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
  28.                 }
  29.             }
  30.             return instance;
  31.         } catch (Throwable t) {
  32.             throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
  33.                     type + ")  could not be instantiated: " + t.getMessage(), t);
  34.         }
  35.     }
复制代码
getAdaptiveExtension 的实现原理

        在getAdaptiveExtension()方法中,会为扩展点接口自动天生实现类字符串,实现类主要包含以下逻辑:为接口中每个有^Adaptive注解的方法天生默实现(没有注解的方法则天生空实现),每个默认实现都会从URL中提取Adaptive参数值,并以此为依据动态加载扩展点。然后,框架会使用不同的编译器,把实现类字符串编译为自适应类并返回。

         天生完代码之后就要对代码进行编译,天生一个新的Classo Dubbo中的编译器也是一个自 
适应接口,但@Adaptive注解是加在实现类AdaptiveCompiler上的。这样一来AdaptiveCompiler 
就会作为该自适应类的默认实现,不必要再做代码天生和编译就可以使用了。
  1.     private Class<?> createAdaptiveExtensionClass() {
  2.         String code = createAdaptiveExtensionClassCode();
  3.         ClassLoader classLoader = findClassLoader();
  4.         com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
  5.         return compiler.compile(code, classLoader);
  6.     }
复制代码
// TODO :待续

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表