Dubbo动态编译

365 阅读2分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

前言

在我们日常的开发中,大多数时候都是编写.java文件,然后通过javac命令将.java文件编译为.class字节码文件,再然后使用Java虚拟机把.class文件加载到虚拟机内存中生成Class对象,最后通过Class对象创建对象实例,此过程中的编译阶段称为静态编译

动态编译通常是在JVM进程运行的过程中,程序生成.java文件,然后JVM动态地把.java文件编译为.class字节码文件。

作用

  • 反射的性能比较低,可以使用动态编译来替代反射,加速程序性能。
  • 很多在线编码、测评的小工具都是通过在浏览器编写.java源文件,然后上传到服务器,进行动态编译,进而创建对象实例运行功能来实现的。

Dubbo动态编译

dubbo框架会为每个扩展接口动态生成适配器类源文件,然后使用动态编译技术生成适配器类实例对象。

在dubbo中,提供了一个Compiler的SPI扩展接口。有两个扩展接口实现类,分别是JavassistCompiler(默认实现类)和JdkCompiler。

@SPI("javassist")
public interface Compiler {
​
    /**
     * 编译java源代码
     *
     * @param code        源代码字符串
     * @param classLoader 类加载器
     * @return            编译后生成的Class对象
     */
    Class<?> compile(String code, ClassLoader classLoader);
​
}

dubbo框架通过ExtensionLoader#createAdaptiveExtensionClass生成源文件并且动态编译为Class类对象

private Class<?> createAdaptiveExtensionClass() {
    // 生成扩展接口对应的适配器类字符串源码
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    
    // 得到compiler接口的实现类
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    
    // 根据源代码字符串进行动态编译,生成Xxx$Adaptive类的Class对象
    return compiler.compile(code, classLoader);
}

生成源代码的方法:org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generate

public String generate() {
    // 如果没有适配方法的话,那么不创建适配器类源代码
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }
​
    StringBuilder code = new StringBuilder();
    // 追加包信息、导入信息、类定义信息
    code.append(generatePackageInfo());
    code.append(generateImports());
    code.append(generateClassDeclaration());
​
    // 生成接口中声明的方法
    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    
    code.append("}");
    return code.toString();
}

以MonitorFactory扩展接口为例,生成的适配器源代码字符串如下(做了些格式化处理,实际源代码紧凑,无换行):

package org.apache.dubbo.monitor;
import org.apache.dubbo.common.extension.ExtensionLoader;
​
public class MonitorFactory$Adaptive implements org.apache.dubbo.monitor.MonitorFactory {
​
    public org.apache.dubbo.monitor.Monitor getMonitor(org.apache.dubbo.common.URL arg0) {
​
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.monitor.MonitorFactory) name from url (" + url.toString() + ") use keys([protocol])");
​
        org.apache.dubbo.monitor.MonitorFactory extension = (org.apache.dubbo.monitor.MonitorFactory) ExtensionLoader.getExtensionLoader(org.apache.dubbo.monitor.MonitorFactory.class).getExtension(extName);
        return extension.getMonitor(arg0);
    }
​
}

执行动态编译并返回适配器类的Class对象,以默认的扩展JavassistCompiler为例。

org.apache.dubbo.common.compiler.support.JavassistCompiler#doCompile

@Override
public Class<?> doCompile(String name, String source) throws Throwable {
    // 创建CtClass构造器对象
    CtClassBuilder builder = new CtClassBuilder();
    builder.setClassName(name);
​
    // 处理import导包部分
    Matcher matcher = IMPORT_PATTERN.matcher(source);
    while (matcher.find()) {
        builder.addImports(matcher.group(1).trim());
    }
    
    // 处理父类
    matcher = EXTENDS_PATTERN.matcher(source);
    if (matcher.find()) {
        builder.setSuperClassName(matcher.group(1).trim());
    }
    
    // 添加需要实现的接口
    matcher = IMPLEMENTS_PATTERN.matcher(source);
    if (matcher.find()) {
        String[] ifaces = matcher.group(1).trim().split("\,");
        Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
    }
    
    //  设置构造器、属性字段、方法
    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);
        }
    });
    
    // 动态编译,返回适配器类的Class对象
    ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());
    CtClass cls = builder.build(classLoader);
    return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}

在生成完适配器类的Class对象之后,后续可以通过java.lang.Class#newInstance创建适配器类的对象实例。程序中调用扩展接口的方法,实际上调用就是适配器类的方法。