dubbo源码阅读之自适应扩展

自适应扩展机制

刚开始看代码,其实并不能很好地理解dubbo的自适应扩展机制的作用,我们不妨先把代码的主要逻辑过一遍,梳理一下,在了解了代码细节之后,回过头再来思考自适应扩展的作用,dubbo为什么要设计这样一种扩展机制??它的优点在什么地方??

Adaptive注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
 * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
 * in the URL, and the parameter names are given by this method.
 * 该值决定了哪个扩展类会被自动注入。目标扩展名由传入的URL中的参数和Adaptive注解中的value值共同决定。
 * value相当于一些key值,用这些key值从URL中获取相应的value值,最终决定扩展名。
 * <p>
 * If the specified parameters are not found from {@link URL}, then the default extension will be used for
 * dependency injection (specified in its interface's {@link SPI}).
 * 如果传入的URL中没有符合条件的扩展名,那么使用默认扩展名(默认扩展名由SPI注解的value决定)
 * <p>
 * For examples, given <code>String[] {"key1", "key2"}</code>:
 * 例如,value的值是:String[] {"key1", "key2"}
 * <ol>
 * <li>find parameter 'key1' in URL, use its value as the extension's name</li>
 * 首先在URL中查找key1,如果存在使用key1作为扩展名
 * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
 * 如果URL中找不到key1,查找key2,如果能找到使用key2作为扩展名
 * <li>use default extension if 'key2' doesn't appear either</li>
 * 如果key2也找不到,那么使用默认扩展名
 * <li>otherwise, throw {@link IllegalStateException}</li>
 * 如果默认扩展名为空,则抛异常
 * </ol>
 * If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
 * class name with the rule: divide classname from capital char into several parts, and separate the parts with
 * dot '.', for example: for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, its default name is
 * <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
 * 如果SPI注解中没有给出默认扩展名,就会通过如下规则生成默认扩展名:将驼峰式风格的接口名转换成.号分隔的名称作为扩展名,例如
 * 接口名org.apache.dubbo.xxx.YyyInvokerWrapper会被转换成yyy.invoker.wrapper作为扩展名。用这个名称在URL参数中查找。
 *
 *
 * @return parameter key names in URL
 */
String[] value() default {};

}

getAdaptiveExtension

我们先从获取自适应扩展类的入口方法看起,

public T getAdaptiveExtension() {
    //检查缓存
    Object instance = cachedAdaptiveInstance.get();
    //如果缓存为空,加载自适应扩展类,并设置缓存
    if (instance == null) {
        //如果之前已经加载过,并且抛出了异常,那么这里就不需要再试,直接抛异常
        if (createAdaptiveInstanceError == null) {
            //惯用法,双检查锁
            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);
                    }
                }
            }
        } else {
            throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}

主要逻辑就是检查缓存,如果缓存不错存在则加载自适应扩展类,使用双检查锁机制保证在多线程的情况下不会重复加载。都是惯用法,没什么可说的。

createAdaptiveExtension

private T createAdaptiveExtension() {
    try {
        //1. 获取自适应扩展类并实例化
        //2. 自动注入属性值
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

这里使用了dubbo实现的IOC功能,ExtensionLoader的IOC特性我们再上一节已经说过,通过ExtensionFactory接口的扩展类实现对属性的自动注入,不再赘述。
我们的重点还是放在如何生成自适应扩展类上,即getAdaptiveExtensionClass的逻辑。

getAdaptiveExtensionClass

private Class<?> getAdaptiveExtensionClass() {
    //首先查找所有 资源文件,加载全部扩展类,但并未实例化
    // 在加载扩展类的过程中,如果遇到带Adaptive注解的类,会把该类设到缓存cachedAdaptiveClass
    //这种情况,由用户来实现自适应扩展的逻辑,比较简单
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //大部分情况下,自适应扩展的逻辑不是由用户实现,而是由框架自动生成的,生成的逻辑就在下面的方法中
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

没啥好说的,继续看下一个方法

createAdaptiveExtensionClass

//重点来了,这个方法定义了生成自适应扩展类代码的总体方案,
//这个方法引出了自适应扩展机制的核心类AdaptiveClassCodeGenerator
private Class<?> createAdaptiveExtensionClass() {
    //AdaptiveClassCodeGenerator类是关键代码
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    //Compiler接口也是通过dubbo自己的spi机制加载的,
    // Compiler接口的自适应扩展类是预先写好的代码,即AdaptiveCompiler
    // 这里有一个有意思的问题,如果compiler的自适应扩展类也由框架自动生成,那么这里就会出现循环调用
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    //返回编译后的Class对象
    return compiler.compile(code, classLoader);
}

AdaptiveClassCodeGenerator.generate

生成自适应扩展类代码的核心逻辑被封装在AdaptiveClassCodeGenerator类中,AdaptiveClassCodeGenerator的构造方法仅仅设置了要扩展的接口类型以及默认扩展类名(可能为空),我们直接从generate方法看起,

/**
 * generate and return class code
 * 生成自适应扩展类代码
 */
public String generate() {
    // no need to generate adaptive class since there's no adaptive method found.
    // 如果接口中没有带Adaptive注解的方法,说名该类型不需要自适应扩展,直接抛异常
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    // 生成package信息,与接口的包名相同
    code.append(generatePackageInfo());
    //生成import信息, 生成了如下的import语句
    //import org.apache.dubbo.common.extension.ExtensionLoader
    code.append(generateImports());
    //生成类定义信息
    // public class <接口名>$Adaptive implements <接口全限定名> {
    code.append(generateClassDeclaration());

    //生成方法信息,这一步是重点
    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    //最后在末尾加上一个花括号
    code.append("}");
    
    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    return code.toString();
}

总体来看,逻辑还是比较简单的,就是我们平时写代码的步骤,依次生成包信息,import内容,类的具体代码包括每个方法的实现代码。
其中比较重要的是生成方法的实现代码,我们重点看一下这段代码。

generateMethod

private String generateMethod(Method method) {
    // 方法的返回类型
    String methodReturnType = method.getReturnType().getCanonicalName();
    // 方法名称
    String methodName = method.getName();
    // 方法体,即方法的实现代码,这一步最关键
    String methodContent = generateMethodContent(method);
    // 方法参数,
    // (ParamType0 arg0, ParamType1 arg1, ParamType2 arg2)
    String methodArgs = generateMethodArguments(method);
    // 异常信息,throws内容
    String methodThrows = generateMethodThrows(method);
    //最后按照java代码的规范拼接成一个完整的方法代码
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

重点是生成方法具体实现代码的逻辑,generateMethodContent方法。

generateMethodContent

生成方法体的逻辑。这个方法比较长,我们分开来看。

/**
 * generate method content
 * 生成方法体
 */
private String generateMethodContent(Method method) {
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    // 这一句应该放到else分支内部
    StringBuilder code = new StringBuilder(512);
    // 如果方法不带Adaptive注解,就直接生成抛Unsupported异常的实现,
    // 也就是说,自适应扩展机制必须要在需要自适应的方法上加Adaptive注解
    if (adaptiveAnnotation == null) {
        return generateUnsupported(method);
    } else {
        // 找到URL类型的参数的下标
        int urlTypeIndex = getUrlTypeIndex(method);

        // found parameter in URL type
        if (urlTypeIndex != -1) {
            // Null Point check
            // 如果方法参数列表中有URL类型的参数,那么添加对参数做非空检查的代码
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            // did not find parameter in URL type
            // 如果方法参数中没有URL类型,那么查找能够简介获取到URL的参数,这个参数类型应该含有返回类型的是URL的方法
            code.append(generateUrlAssignmentIndirectly(method));
        }

        // 获取Adaptive注解的value
        // 如果Adaptive注解的value为空,获取会麻烦一些,将接口的驼峰式名称转换成.分隔的名称
        // 如LoadBalance转换成load.balance
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);

        // 检查Invocation类型的参数
        boolean hasInvocation = hasInvocationArgument(method);

        // 生成检查Invocation类型参数非空的代码段,
        // 只检查第一个参数??Invocation类型的参数只会有一个??
        code.append(generateInvocationArgumentNullCheck(method));

        // 生成获取扩展名的代码段,
        // 扩展名就是资源文件中以k-v存储的key值
        code.append(generateExtNameAssignment(value, hasInvocation));
        // check extName == null?
        // 生成扩展名非空检查语句
        code.append(generateExtNameNullCheck(value));

        // 获取到扩展名后,就可以生成扩展类的获取语句了
        code.append(generateExtensionAssignment());

        // return statement
        // 最后加上函数调用和返回语句
        code.append(generateReturnAndInovation(method));
    }

    return code.toString();
}

生成的方法体大致分为三块

  • 获取URL的值。如果参数中有URL类型直接赋值;如果没有就间接获取,查找参数中包含返回URL类型的方法,调用这个方法。此外加上一些非空判断语句。
  • 获取扩展名。这一步比较复杂,要考虑的情况比较多。如果Adaptive注解中有protocol就要调用URL的getProtocol方法;用以Adaptive的value数组中的key值依次去URL中获取参数,直到获取到不为空的参数。加上扩展名的非空检查语句。
  • 通过ExtensionLoader获取扩展名对应的扩展类,赋值给变量extension
  • 最后调用扩展类的相应方法,如果返回值不是void就加上return。

其中获取扩展名代码的生成逻辑较为复杂,我们看一下这段代码。

generateExtNameAssignment

private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
    // TODO: refactor it
    String getNameCode = null;
    // 从后向前遍历可选的扩展名
    for (int i = value.length - 1; i >= 0; --i) {
        // 对于最后一个value值做如下处理
        if (i == value.length - 1) {
            if (null != defaultExtName) {
                // 如果不是protocol
                if (!"protocol".equals(value[i])) {
                    if (hasInvocation) {
                        // 如果存在Invocation参数,生成获取方法参数的代码段
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                    }
                } else {
                    // 如果是protocol,生成扩区protocol的代码段
                    getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                }
            } else {
                // 如果默认扩展名为空,在生成的代码中不使用带有默认扩展名的方法
                if (!"protocol".equals(value[i])) {
                    if (hasInvocation) {
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                    }
                } else {
                    getNameCode = "url.getProtocol()";
                }
            }
            //对于不是最后一个的value值做如下处理
        } else {
            if (!"protocol".equals(value[i])) {
                if (hasInvocation) {
                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                } else {
                    getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                }
            } else {
                getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
            }
        }
    }

    // 拼接成赋值语句
    // String extName = %s;
    return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}

代码生成示例

这样光看代码很难对这块代码有直观的认识,理解也不够深刻,既然这段代码的作用主要是生成代码,那么我们就定义一个自己的接口,并实现一个扩展类,用AdaptiveClassCodeGenerator来生成一下,看生成的代码到底是什么样子。

  • 首先是接口

      package org.apache.dubbo.common.extension.adaptive.codeGen;
    
      import org.apache.dubbo.common.URL;
      import org.apache.dubbo.common.extension.Adaptive;
      import org.apache.dubbo.common.extension.SPI;
      
      @SPI("default")
      public interface MyExtension {
      
      @Adaptive
      String adaptiveMethod(String param1, int param2, URL url) throws Exception;
      
      @Adaptive("protocol")
      String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception;
      
      void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception;
      }
    
  • 实现类

      package org.apache.dubbo.common.extension.adaptive.codeGen.impl;
    
      import org.apache.dubbo.common.URL;
      import org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension;
      import org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter;
      
      public class DefaultMyExtension implements MyExtension {
          @Override
          public String adaptiveMethod(String param1, int param2, URL url) {
              return String.format("Params passed in are: param1:%s, param2:%d, url:%s");
          }
      
          @Override
          public String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception {
              return String.format("Params passed in are: param1:%s, param2:%d, urlGetter:%s");
          }
      
          @Override
          public void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception {
      
          }
      }
    
  • URL获取类。为了测试简介获取URL

      package org.apache.dubbo.common.extension.adaptive.codeGen;
      
      import org.apache.dubbo.common.URL;
      
      public class URLGetter {
          private URL url;
      
          public URLGetter(URL url){
              this.url=url;
          }
      
          public URL getUrl() {
              return url;
          }
      
          public void setUrl(URL url) {
              this.url = url;
          }
      
          @Override
          public String   toString() {
              return "URLGetter{" +
                      "url=" + url +
                      '}';
          }
      }
    
  • 测试类

      package org.apache.dubbo.common.extension.adaptive.codeGen;
      
      import org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator;
      import org.junit.jupiter.api.Test;
      
      public class AdaptiveClassCodeGeneratorTest {
      
          @Test
          public void testProtocolCodeGen() {
              AdaptiveClassCodeGenerator generator=
                      new AdaptiveClassCodeGenerator(MyExtension.class,"default");
              String code=generator.generate();
              System.out.println(code);
          }
      }
    
  • 生成的代码。也就是自适应扩展类

      package org.apache.dubbo.common.extension.adaptive.codeGen;
      
      import org.apache.dubbo.common.extension.ExtensionLoader;
      
      public class MyExtension$Adaptive implements org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension {
          public java.lang.String adaptiveMethod(java.lang.String arg0, int arg1, org.apache.dubbo.common.URL arg2) throws java.lang.Exception {
              if (arg2 == null) throw new IllegalArgumentException("url == null");
              org.apache.dubbo.common.URL url = arg2;
              // Adaptive注解中没有value,用接口名称自动生成的key值
              String extName = url.getParameter("my.extension", "default");
              if (extName == null)
                  throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([my.extension])");
              org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName);
              return extension.adaptiveMethod(arg0, arg1, arg2);
          }
      
          public java.lang.String adaptiveMethod2(java.lang.String arg0, int arg1, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg2) throws java.lang.Exception {
              if (arg2 == null)
                  throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument == null");
              if (arg2.getUrl() == null)
                  throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument getUrl() == null");
              // 间接获取URL
              org.apache.dubbo.common.URL url = arg2.getUrl();
              // 如果有protocol的key,那么调用URL.getProtocol方法获取协议名称
              String extName = (url.getProtocol() == null ? "default" : url.getProtocol());
              if (extName == null)
                  throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([protocol])");
              org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName);
              return extension.adaptiveMethod2(arg0, arg1, arg2);
          }
      
          // 为标Adaptive注解的方法直接抛异常
          public void noAdaptiveMethod(java.lang.String arg0, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg1) throws java.lang.Exception {
              throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.noAdaptiveMethod(java.lang.String,org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter) throws java.lang.Exception of interface org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension is not adaptive method!");
          }
      }
    

这样看是不是直观多了。回过头再去看一遍代码生成逻辑,就好懂多了。

编译加载

代码生成之后,还需要将代码编译为字节码并通过类加载器加载到虚拟机中,获取Class对象,然后才能使用。
负责编译的是org.apache.dubbo.common.compiler.Compiler接口,这个接口的实现类也是通过ExtensionLoader进行加载的,见ExtensionLoader.createAdaptiveExtensionClass方法。
我们看一下Compiler接口

Compiler

// 默认扩展名是javassist
@SPI("javassist")
public interface Compiler {

    /**
     * Compile java source code.
     *
     * @param code        Java source code
     * @param classLoader classloader
     * @return Compiled class
     */
    Class<?> compile(String code, ClassLoader classLoader);

}

另外,我们再看一下dubbo-common模块中META-INF/dubbo/inetrnal/org.apache.dubbo.common.compiler.Compiler文件中的内容:

adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler

AdaptiveCompiler类上带有Adaptive注解,所以ExtensionLoader在加载时AdaptiveCompiler类会被设置为自适应扩展类。而在createAdaptiveExtensionClass方法中真是通过Compiler接口的自适应扩展类来进行编译。那么我们看一下AdaptiveCompiler中的逻辑。

AdaptiveCompiler

public Class<?> compile(String code, ClassLoader classLoader) {
    Compiler compiler;
    ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
    // 可以通过设置静态变量DEFAULT_COMPILER指定默认扩展类
    String name = DEFAULT_COMPILER; // copy reference
    if (name != null && name.length() > 0) {
        compiler = loader.getExtension(name);
    } else {
        // 如果未指定,则获取SPI注解指定的默认扩展类
        compiler = loader.getDefaultExtension();
    }
    return compiler.compile(code, classLoader);
}

由Compiler接口上的SPI注解,我们知道默认的实现类是JavassistCompiler,所以我们看一下这个类是怎么进行编译的。

JavassistCompiler

JavassistCompiler继承自AbstractCompiler,有一部分逻辑在AbstractCompiler类中,所以我们先看一下

AbstractCompiler.compile

private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");

private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");    

public Class<?> compile(String code, ClassLoader classLoader) {
    //去除两端空格
    code = code.trim();
    // 匹配包名
    Matcher matcher = PACKAGE_PATTERN.matcher(code);
    String pkg;
    if (matcher.find()) {
        pkg = matcher.group(1);
    } else {
        pkg = "";
    }
    // 匹配类名
    matcher = CLASS_PATTERN.matcher(code);
    String cls;
    if (matcher.find()) {
        cls = matcher.group(1);
    } else {
        throw new IllegalArgumentException("No such class name in " + code);
    }
    // 根据包名和类名拼接类的全限定名
    String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;
    try {
        // 尝试直接加载???类的字节码已经存在??或者类已经被加载过了??
        // 什么情况下会出现这种情况??
        return Class.forName(className, true, ClassHelper.getCallerClassLoader(getClass()));
    } catch (ClassNotFoundException e) {
        // 感觉没必要多这一句吧??
        // 代码合法性检查应该在生成代码的方法中做,这地方单独做一个花括号检查没什么意义吧?
        if (!code.endsWith("}")) {
            throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");
        }
        try {
            // 由子类实现
            return doCompile(className, code);
        } catch (RuntimeException t) {
            throw t;
        } catch (Throwable t) {
            throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));
        }
    }
}

主要就是拼接处类的全限定名,然后做一些检查,尝试直接加载类。真正的编译过程交由子类实现。

JavassistCompiler.doCompile

@Override
public Class<?> doCompile(String name, String source) throws Throwable {
    // 实际执行编译的类
    CtClassBuilder builder = new CtClassBuilder();
    // 设置类名
    builder.setClassName(name);

    // process imported classes
    // 匹配出所有的import语句,并设置到CtClassBuilder对象中
    Matcher matcher = IMPORT_PATTERN.matcher(source);
    while (matcher.find()) {
        builder.addImports(matcher.group(1).trim());
    }
    
    // process extended super class
    // 匹配出extends的类型,设置到CtClassBuilder对象中
    matcher = EXTENDS_PATTERN.matcher(source);
    if (matcher.find()) {
        builder.setSuperClassName(matcher.group(1).trim());
    }
    
    // process implemented interfaces
    // 匹配出implements的类型,设置到CtClassBuilder对象中
    matcher = IMPLEMENTS_PATTERN.matcher(source);
    if (matcher.find()) {
        String[] ifaces = matcher.group(1).trim().split("\\,");
        Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
    }
    
    // process constructors, fields, methods
    // 添加方法和域
    String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
    String[] methods = METHODS_PATTERN.split(body);
    String className = ClassUtils.getSimpleClassName(name);
    Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method-> {
        if (method.startsWith(className)) {
            builder.addConstructor("public " + method);
        } else if (FIELD_PATTERN.matcher(method).matches()) {
            builder.addField("private " + method);
        } else {
            builder.addMethod("public " + method);
        }
    });
    
    // compile
    ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());
    // 进行编译,实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去
    CtClass cls = builder.build(classLoader);
    return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}

实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去。

总结

自适应扩展模块主要分为两块,一是生成自适应扩展类的代码,二是将代码进行编译并加载编译后的字节码。

  • 其中dubbo实现的主要逻辑就是代码生成。
    自适应加载扩展类的主要思路就是从方法的参数中获取URL参数,可以是方法参数本省,也可以从参数的类型的方法中获取;
    然后从URL参数中查找扩展名(查找的范围通过Adaptive注解的value值和SPI注解中的默认值组成),找到扩展名后再由ExtensionLoader根据扩展名加载对应的扩展类,
    然后调用对应扩展类的对应方法并返回。

  • 编译代码的逻辑主要有javassist库实现,dubbo在javassist库基础上进行了一些简单封装,大部分的逻辑都是直接调用javassist库,这部分不再深入下去,如果对java代码编译感兴趣可以细看。

posted on 2019-05-01 19:10  _朱葛  阅读(279)  评论(0编辑  收藏  举报