文章目录
本文主要参考自Dubbo官方文档、Dubbo项目源码以及网络文章和相关书籍,并附上自身的一些理解,如有遗漏或错误,还望海涵并指出。谢谢!
------本文基于Dubbo-2.6.1版本
一.Javassist动态编译
1.1、Javassist介绍
Javassist是一款Java字节码生成和转化框架,它在Dubbo中起到的作用是动态编译以及动态代理。
为什么需要使用Javassist作为动态编译和代理框架呢,原因主要Javassist的性能以及效率都高于JDK自身的框架。
譬如,如果需要在运行期获取一个特定的类可以采用JDK反射来实现,但是使用反射会有一个缺点—效率低,所以在一些追求效率的场合会使用Javassist直接在内存中生成class字节码对象供程序来使用,这就是Dubbo使用Javassist的原因。
1.2、入门使用
下面编写一个入门Demo来演示一下如何使用Javassist在内存中生成一个class,并且将其字节码输出到文件中保存起来:
- TestJavassist
@Test
public void test_Javassist() throws CannotCompileException, NotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassPool pool = ClassPool.getDefault();
CtClass cls = pool.makeClass("thinking_in_java.javassist.proxyClass");
// 添加私有成员name及其getter、setter方法
CtField param = new CtField(pool.get("java.lang.String"), "name", cls);
param.setModifiers(Modifier.PRIVATE);
cls.addMethod(CtNewMethod.setter("setName", param));
cls.addMethod(CtNewMethod.getter("getName", param));
cls.addField(param, CtField.Initializer.constant(""));
// 添加无参的构造体
CtConstructor cons = new CtConstructor(new CtClass[] {}, cls);
cons.setBody("{name = \"ARong\";}");
cls.addConstructor(cons);
// 添加有参的构造体
cons = new CtConstructor(new CtClass[] {pool.get("java.lang.String")}, cls);
cons.setBody("{$0.name = $1;}");
cls.addConstructor(cons);
// 打印创建类的类名
System.out.println(cls.toClass());
// 输出字节码到class中
byte[] bytes = cls.toBytecode();
FileOutputStream fos = new FileOutputStream(new File("/Users/arong/MyFile/Project/Algorithm/src/main/java/thinking_in_java/javassist/proxyClass.class"));
fos.write(bytes);
fos.flush();
Object o = Class.forName("thinking_in_java.javassist.proxyClass").newInstance();
Method getName = o.getClass().getMethod("getName");
Object res = getName.invoke(o);
System.out.println(o);
System.out.println(res);
}
输出:
class thinking_in_java.javassist.proxyClass
thinking_in_java.javassist.proxyClass@dfd3711
ARong
在文件夹中查看生成的class字节码文件(idea自动反编译成了Java代码):
- proxyClass.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package thinking_in_java.javassist;
public class proxyClass {
private String name = "";
public void setName(String var1) {
this.name = var1;
}
public String getName() {
return this.name;
}
public proxyClass() {
this.name = "ARong";
}
public proxyClass(String var1) {
this.name = var1;
}
}
二.Dubbo动态编译源码解析
看完了Javassist,我们大概知道其实这个框架的作用就是为了代替反射,用更高效的方式直接在内存中生成所需的class。
在Dubbo中也大量地使用了Javassist来做动态编译,譬如在SPI机制中的核心类ExtensionLoader中的生成自适应拓展实现类的createAdaptiveExtensionClass方法就使用了动态编译来生成适应类,不过Dubbo基于Javassist做了一层封装:
- ExtensionLoader
/*
* 创建自适应拓展类的代码并进行编译返回,生成Xxx$Adaptive自适应类(动态编译)
*/
private Class<?> createAdaptiveExtensionClass() {
// 生成自适应类的代码字符串
String code = createAdaptiveExtensionClassCode();
// 获取ClassLoader
ClassLoader classLoader = findClassLoader();
// 对代码进行编译,生成相应的Class返回
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
3.1、createAdaptiveExtensionClassCode方法
在这个方法中生成了Xxx&Adaptive自适应实现类的代码字符串,可供后续被Dubbo编译成真正的Class类:
- createAdaptiveExtensionClassCode
/*
* 生成自适应拓展类的代码
*/
private String createAdaptiveExtensionClassCode() {
// 使用StringBuilder来构建代码字符串
StringBuilder codeBuidler = new StringBuilder();
// 检测方法上是否含有@Adaptive注解,不含有则抛出异常
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// no need to generate adaptive class since there's no adaptive method found.
if (!hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
// 开始构建package、import内容以及类名(Xxx$Adaptive)
codeBuidler.append("package " + type.getPackage().getName() + ";");
codeBuidler.append("\nimport " + ExtensionLoader.class.getName() + ";");
codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adaptive" + " implements " + type.getCanonicalName() + " {");
// 生成代理所含有的方法
// 省略...
return codeBuidler.toString();
}
想更深入地了解具体的代码生成逻辑可以看官方文档关于这个方法的解析:
https://dubbo.apache.org/zh-cn/docs/source_code_guide/adaptive-extension.html
在执行完这个方法之后,就会生成一个自适应拓展类的Java代码字符串了,类似于下面这个形式:
- Ext2$Adaptive
package com.alibaba.dubbo.common.extensionloader.ext2;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Ext2$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext2.Ext2 {
public java.lang.String bang(com.alibaba.dubbo.common.URL arg0, int arg1) {throw new UnsupportedOperationException("method public abstract java.lang.String com.alibaba.dubbo.common.extensionloader.ext2.Ext2.bang(com.alibaba.dubbo.common.URL,int) of interface com.alibaba.dubbo.common.extensionloader.ext2.Ext2 is not adaptive method!");
}
public java.lang.String echo(com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder arg0, java.lang.String arg1) {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("ext2");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext2.Ext2) name from url(" + url.toString() + ") use keys([ext2])");
com.alibaba.dubbo.common.extensionloader.ext2.Ext2 extension = (com.alibaba.dubbo.common.extensionloader.ext2.Ext2)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext2.Ext2.class).getExtension(extName);
return extension.echo(arg0, arg1);
}
}
3.2、Compiler接口
接下来就是重点了:如何将Java代码字符串编译成可供使用的Class?如下面的代码:
- ExtensionLoader
/*
* 创建自适应拓展类的代码并进行编译返回,生成Xxx$Adaptive自适应类(动态编译)
*/
private Class<?> createAdaptiveExtensionClass() {
// 生成自适应类的代码字符串
String code = createAdaptiveExtensionClassCode();
// 获取ClassLoader
ClassLoader classLoader = findClassLoader();
// 对代码进行编译,生成相应的Class返回
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
这就是Complier接口做的事情,Complier接口的全限定名:
com.alibaba.dubbo.common.compiler.Compiler
- 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接口也是使用了SPI机制的,其默认拓展实现类为javassist这个拓展名映射的实现类,
查阅resource/META-INF/dubbo查看Compiler接口所有拓展实现类:
- META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler.Compiler
adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

有效代码量750行左右:

3.2.1、AdaptiveCompiler
com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler ,实现 Compiler 接口,自适应 Compiler 实现类。
- AdaptiveComiler
@Adaptive
public class AdaptiveCompiler implements Compiler {
// 默认的编译器名称
private static volatile String DEFAULT_COMPILER;
// 可指定使用编译器
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
// 获取ExtensionLoader
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
// 编译器名称设置为默认
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
// 使用指定的编译器
compiler = loader.getExtension(name);
} else {
// 使用默认编译器(Javassist)
compiler = loader.getDefaultExtension();
}
// 编译生成Class
return compiler.compile(code, classLoader);
}
}
从AdaptiveCompiler的代码中可以看出,获取到自适应的Compiler,默认情况下也是获取到Javassist作为拓展实现的,当然也可以指定其他实现,譬如JDK。
如果需要指定JDK Compiler,可通过下述配置:
<dubbo:application compiler="jdk" />
这个配置会触发ApplicationConfig的setCompiler方法,该方法会调用setDefaultCompiler设置编译器:
- ApplicationConfig
public void setCompiler(String compiler) {
this.compiler = compiler;
AdaptiveCompiler.setDefaultCompiler(compiler);
}
3.2.2、AbstractCompiler
com.alibaba.dubbo.common.compiler.support.AbstractCompiler是Compiler接口的实现类,使用模板方法模式,定义doCompile方法,实质上依赖于JavassistCompiler或JdkCompiler的实现:
- AbstractCompiler
public abstract class AbstractCompiler implements Compiler {
// 定义package正则匹配
private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");
// 定义class正则匹配
private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");
/*
* 编译code生成Class
*/
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 {
// 尝试使用反射直接加载该Class,如果不存在则抛出异常,进入catch块中
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 {
// 模板方法模式
// 使用子类实现的doCompile方法编译生成Class
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));
}
}
}
/*
* 由子类提供具体实现
*/
protected abstract Class<?> doCompile(String name, String source) throws Throwable;
}
3.2.3、JavassistCompiler
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]+;");
/*
* name:类全限定名
* source:Java代码
*/
@Override
public Class<?> doCompile(String name, String source) throws Throwable {
int i = name.lastIndexOf('.');
// 获取到类名称
String className = i < 0 ? name : name.substring(i + 1);
// 创建Javassist ClassPool,开始构建Class
ClassPool pool = new ClassPool(true);
// 设置类搜索路径
pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));
// 匹配 import
Matcher matcher = IMPORT_PATTERN.matcher(source);
// 引用的包名
List<String> importPackages = new ArrayList<String>();
// 引用的类名
Map<String, String> fullNames = new HashMap<String, String>();
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);
}
}
}
// 获取到所有需要import的类
String[] packages = importPackages.toArray(new String[0]);
matcher = EXTENDS_PATTERN.matcher(source);
CtClass cls;
if (matcher.find()) {
String extend = matcher.group(1).trim();
String extendClass;
if (extend.contains(".")) {
extendClass = extend;
} else if (fullNames.containsKey(extend)) {
extendClass = fullNames.get(extend);
} else {
extendClass = ClassUtils.forName(packages, extend).getName();
}
// 为cls添加extends的类
cls = pool.makeClass(name, pool.get(extendClass));
} else {
cls = pool.makeClass(name);
}
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(".")) {
ifaceClass = iface;
} else if (fullNames.containsKey(iface)) {
ifaceClass = fullNames.get(iface);
} else {
ifaceClass = ClassUtils.forName(packages, iface).getName();
}
// 为cls添加implement的接口
cls.addInterface(pool.get(ifaceClass));
}
}
// 开始匹配类的主体构造方法、public方法和private方法
String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
// 匹配完方法,并为cls添加上去
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));
}
}
}
// 通过cls创建Class
return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
}
}
318



被折叠的 条评论
为什么被折叠?



