以下我们来研究一下dubbo的动态编译
类图:
接口定义:
@SPI("javassist") public interface Compiler { /** * Compile java source code. * * @param code Java source code * @param classLoader TODO * @return Compiled class */ Class<?> compile(String code, ClassLoader classLoader); }
SPI注解表示如果没有配置,dubbo默认选用javassist编译源代码
code:Java源代码
classLoader:类加载器用来加载编译后的字节码(好像没有使用),在抽象类AbstractCompiler中的compile方法中使用ClassHelper.getCallerClassLoader(getClass())来加载类。
AdaptiveCompiler类是Compiler的适配类,利用SPI机制来查找真正的实现类。
public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader);//调用AbstractCompiler中的方法 }
AbstractCompiler:在公用逻辑中,利用正则表达式匹配出源代码中的包名和类名:
PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");
CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");
然后在JVM中查找看看是否存在:Class.forName(className, true, ClassHelper.getCallerClassLoader(getClass()));存在返回,不存在就使用JavassistCompiler或者是JdkCompiler来执行编译。
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);//调用实现类JavassistCompiler或JdkCompiler的doCompile方法来动态编译类 } 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和JdkCompiler真正的实现类:就是利用Javassit和Jdk提供的相关api或者扩展接口实现的。