我们运行的Java代码,一般都是编译之后的字节码。Dubbo为了实现基于SPI思想的扩展特性,可以灵活的添加额外的功能。对于SPI接口需要能够动态生成,这样就需要在运行的时候去编译加载这个设配类的代码。下面我们就是来了解下Dubbo的动态编译。

    我们首先来看一下Compile的类图。

    Dubbo源码(二) 动态编译 - 图1

    Compile接口定义:

    1. @SPI("javassist")
    2. public interface Compiler {
    3. /**
    4. * Compile java source code.
    5. *
    6. * @param code Java source code
    7. * @param classLoader TODO
    8. * @return Compiled class
    9. */
    10. Class<?> compile(String code, ClassLoader classLoader);
    11. }
    • @SPI(“javassist”):表示如果没有配置,dubbo默认选用javassist编译源代码
    • 接口方法compile第一个入参code,就是java的源代码
    • 接口方法compile第二个入参classLoader,按理是类加载器,用来加载编译后的字节码,其实没用到,都是根据当前线程或者调用方的classLoader加载的

    在上面一个章节内核SPI。我们说过对于dubbo SPI通过@SPI注解与@Adaptive实现,它有两种实现方式。
    Dubbo源码(一) 内核SPI实现

    • @Adaptive注解在标注@SPI接口的方法上,扩展类就是通过Compile字节码技术动态编译。

    编译后的代码模板如下所示。

    1. package <扩展点接口所在包>;
    2. public class <扩展点接口名>$Adpative implements <扩展点接口> {
    3. public <有@Adaptive注解的接口方法>(<方法参数>) {
    4. if(是否有URL类型方法参数?) 使用该URL参数
    5. else if(是否有方法类型上有URL属性) 使用该URL属性
    6. # <else 在加载扩展点生成自适应扩展点类时抛异常,即加载扩展点失败!>
    7. if(获取的URL == null) {
    8. throw new IllegalArgumentException("url == null");
    9. }
    10. 根据@Adaptive注解上声明的Key的顺序,从URL获致Value,作为实际扩展点名。
    11. URL没有Value,则使用缺省扩展点实现。如没有扩展点, throw new IllegalStateException("Fail to get extension");
    12. 在扩展点实现调用该方法,并返回结果。
    13. }
    14. public <有@Adaptive注解的接口方法>(<方法参数>) {
    15. throw new UnsupportedOperationException("is not adaptive method!");
    16. }
    17. }

    例如com.alibaba.dubbo.rpc.Protocol接口的动态编译的扩展类Protocol$Adpative为:

    1. package com.alibaba.dubbo.rpc;
    2. import com.alibaba.dubbo.common.extension.ExtensionLoader;
    3. public class Protocol$Adpative implements Protocol {
    4. public void destroy() {
    5. throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    6. }
    7. public int getDefaultPort() {
    8. throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    9. }
    10. public com.alibaba.dubbo.rpc.Exporter export(Invoker invoker) throws RpcException {
    11. if (invoker == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
    12. if (invoker.getUrl() == null)
    13. throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
    14. com.alibaba.dubbo.common.URL url = invoker.getUrl();
    15. String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
    16. if (extName == null)
    17. throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    18. Protocol extension = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    19. return extension.export(invoker);
    20. }
    21. public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws RpcException {
    22. if (arg1 == null) throw new IllegalArgumentException("url == null");
    23. com.alibaba.dubbo.common.URL url = arg1;
    24. String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
    25. if (extName == null)
    26. throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    27. com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    28. return extension.refer(arg0, arg1);
    29. }
    30. }
    • @Adaptive注解在标注@SPI接口的实现类上,扩展类就是这个类。
      例如这里的Compile的SPI扩展就是AdaptiveCompiler这个类。
    1. @Adaptive
    2. public class AdaptiveCompiler implements Compiler {
    3. private static volatile String DEFAULT_COMPILER;
    4. public static void setDefaultCompiler(String compiler) {
    5. DEFAULT_COMPILER = compiler;
    6. }
    7. public Class<?> compile(String code, ClassLoader classLoader) {
    8. Compiler compiler;
    9. ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
    10. String name = DEFAULT_COMPILER; // copy reference
    11. if (name != null && name.length() > 0) {
    12. compiler = loader.getExtension(name);
    13. } else {
    14. compiler = loader.getDefaultExtension();
    15. }
    16. return compiler.compile(code, classLoader);
    17. }
    18. }

    AdaptiveCompiler是Compiler的设配类,它的作用是Compiler策略的选择,根据条件选择使用何种编译策略来编译动态生成SPI扩展 ,默认为javassist.

    AbstractCompiler是一个抽象类,它通过正则表达式获取到对象的包名以及Class名称。这样就可以获取对象的全类名(包名+Class名称)。通过反射Class.forName()来判断当前ClassLoader是否有这个类,如果有就返回,如果没有就通过JdkCompiler或者JavassistCompiler通过传入的code编译这个类。

    Java字节码框架 Javassist使用
    dubbo-adpative – dubbo spi adpative动态编译代码github地址