在 Java 语言中,大多数情况下,我们已经编写好 Java 类,并编译成 Class 文件进行运行。但是在一些场景下,例如动态代理,需要运用到动态编译的技术
例如,SPI中的 createAdaptiveExtensionClassCode方法中,我们可以看到如下代码:
/**
* 自动生成自适应拓展的代码实现,并编译后返回该类。
*
* @return 类
*/
private Class> createAdaptiveExtensionClass() {
// 自动生成自适应拓展的代码实现的字符串
String code = createAdaptiveExtensionClassCode();
// 编译代码,并返回该类
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
调用 Compiler#compile(code, classLoader) 方法,编译代码,并返回该类。Compiler 基于 Dubbo SPI 机制进行加载,目前有两种实现:
JdkCompiler 和JavassistCompiler
动态编译,在 dubbo-common 模块的 compiler 包下实现,整体类结构如下图:
编辑器接口。代码如下:
@SPI("javassist")
public interface Compiler {
/**
* Compile java source code.
*
* 编译 Java 代码字符串
*
* @param code Java source code
* Java 代码字符串
* @param classLoader classloader
* 类加载器
* @return Compiled class
* 编译后的类
*/
Class> compile(String code, ClassLoader classLoader);
}
实现 Compiler 接口,自适应 Compiler 实现类。
@Adaptive
public class AdaptiveCompiler implements Compiler {
/**
* 默认编辑器的拓展名
*/
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
@Override public Class> compile(String code, ClassLoader classLoader) {
Compiler compiler;
// 获得 Compiler 的 ExtensionLoader 对象。
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
// 使用设置的拓展名,获得 Compiler 拓展对象
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
// 获得默认的 Compiler 拓展对象
} else {
compiler = loader.getDefaultExtension();
}
// 编译类
return compiler.compile(code, classLoader);
}
}
实现 Compiler 接口,Compiler 抽象类。代码如下:
public abstract class AbstractCompiler implements Compiler {
/**
* 正则 - 包名
*/
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())); // classloader 为调用方的
} 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));
}
}
}
/**
* 编译代码
*
* @param name 类名
* @param source 代码
* @return 编译后的类
* @throws Throwable 发生异常
*/
protected abstract Class> doCompile(String name, String source) throws Throwable;
}
Javassist 是一个开源的分析、编辑和创建 Java 字节码的类库
public class JavassistCompiler extends AbstractCompiler {
/**
* 正则 - 匹配 import
*/
private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");
/**
* 正则 - 匹配 extends
*/
private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");
/**
* 正则 - 匹配 implements
*/
private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");
/**
* 正则 - 匹配方法
*/
private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");
/**
* 正则 - 匹配变量
*/
private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");
@Override
public Class> doCompile(String name, String source) throws Throwable {
// 获得类名
int i = name.lastIndexOf('.');
String className = i < 0 ? name : name.substring(i + 1);
// 创建 ClassPool 对象
ClassPool pool = new ClassPool(true);
// 设置类搜索路径
pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));
// 匹配 import
Matcher matcher = IMPORT_PATTERN.matcher(source);
List importPackages = new ArrayList(); // 引用的包名
Map fullNames = new HashMap(); // 引用的类名
while (matcher.find()) {
String pkg = matcher.group(1);
if (pkg.endsWith(".*")) { // 引用整个包下的类/接口
String pkgName = pkg.substring(0, pkg.length() - 2);
pool.importPackage(pkgName);
importPackages.add(pkgName);
} else { // 引用指定类/接口
int pi = pkg.lastIndexOf('.');
if (pi > 0) {
String pkgName = pkg.substring(0, pi);
pool.importPackage(pkgName);
importPackages.add(pkgName);
fullNames.put(pkg.substring(pi + 1), pkg); // 类名
}
}
}
String[] packages = importPackages.toArray(new String[0]);
// 匹配 extends
matcher = EXTENDS_PATTERN.matcher(source);
CtClass cls;
if (matcher.find()) {
String extend = matcher.group(1).trim();
String extendClass;
if (extend.contains(".")) { // 内嵌的类,例如:extends A.B
extendClass = extend;
} else if (fullNames.containsKey(extend)) { // 指定引用的类
extendClass = fullNames.get(extend);
} else { // 引用整个包下的类
extendClass = ClassUtils.forName(packages, extend).getName();
}
// 创建 CtClass 对象
cls = pool.makeClass(name, pool.get(extendClass));
} else {
// 创建 CtClass 对象
cls = pool.makeClass(name);
}
// 匹配 implements
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\\,");
for (String iface : ifaces) {
iface = iface.trim();
String ifaceClass;
if (iface.contains(".")) { // 内嵌的接口,例如:extends A.B
ifaceClass = iface;
} else if (fullNames.containsKey(iface)) { // 指定引用的接口
ifaceClass = fullNames.get(iface);
} else { // 引用整个包下的接口
ifaceClass = ClassUtils.forName(packages, iface).getName();
}
// 添加接口
cls.addInterface(pool.get(ifaceClass));
}
}
// 获得类中的内容,即首末 {} 的内容。
String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
// 匹配 method 。使用分隔的方式,实际上,分隔出来的不仅仅有方法。
String[] methods = METHODS_PATTERN.split(body);
for (String method : methods) {
method = method.trim();
if (method.length() > 0) {
if (method.startsWith(className)) { // 构造方法
cls.addConstructor(CtNewConstructor.make("public " + method, cls));
} else if (FIELD_PATTERN.matcher(method).matches()) { // 变量
cls.addField(CtField.make("private " + method, cls));
} else { // 方法
cls.addMethod(CtNewMethod.make("public " + method, cls));
}
}
}
// 生成类
// JavassistCompiler.class.getProtectionDomain() =》 设置保护域和 JavassistCompiler 一致,即 `#getClass()` 方法。深入见 《Java安全——安全管理器、访问控制器和类装载器》https://www.zybuluo.com/changedi/note/417132
return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
}
}