本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
前言
在我们日常的开发中,大多数时候都是编写.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创建适配器类的对象实例。程序中调用扩展接口的方法,实际上调用就是适配器类的方法。