dubbo动态编译compiler

Posted 码云大作战

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo动态编译compiler相关的知识,希望对你有一定的参考价值。

一、Dubbo的动态编译

    上一篇提到过@Adaptive注解的作用:被@Adaptive修饰的类实际上是一个装饰类。被@Adaptive修饰的方法则会生成一个动态代理类,而根据模板生成的类则需要通过动态编译由字节流被编译成动态代理类。本文主要讲的就是dubbo的动态编译。
    dubbo-spi的扩展装饰类是通过ExtensionLoader.getAdaptiveExtension来获取,内部则进行了动态编译。核心代码如下:

private Class<?> createAdaptiveExtensionClass() {

    //创建动态代理模板 - 即需要编译代理类的内容
String code = createAdaptiveExtensionClassCode();

ClassLoader classLoader = findClassLoader();

    //加载SPI中的compiler编译对象

    Compiler compiler=

ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();

     //进行动态编译

    return compiler.compile(code, classLoader);

}


二、compiler

    首先compiler是一个装饰类,不会生成动态代理类。因为compiler的实现为:AbstractCompiler和AdaptiveCompiler,而在AdaptiveCompiler中,@Adaptive注解没有修饰在方法中,所以不会生成动态代理对象,不需要进行动态编译。

    另外,下图可以看到Compiler默认使用javassist字节码编译方式。


·  AdaptiveCompiler.compile

public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler
;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);

String name = DEFAULT_COMPILER;

//name如果存在则取compiler

if (name != null && name.length() > 0) {
compiler = loader.getExtension(name)
;

} else {

//name如果不存在则取默认配置compiler,即JavassistCompiler

compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}

    上述代码主要是获取compiler的实现类,因为我们没有设置name,所以默认取javassistCompiler。


·  javassistCompiler

    javassist是一款字节码编译工具,也是一个动态类库,它可以直接检测、修改以及创建java类。

public Class<?> compile(String code, ClassLoader classLoader) {

    //...

    //获取要编译的 类名,如Protocol$Adaptive

String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;

try {

//通过类名 反射创建实例,由于Protocol$Adaptive是动态生成出来的,

        //不存在java源码中,因此创建实例,肯定会报ClassNotFound错

return Class.forName(className, true, ClassHelper.getCallerClassLoader(getClass()));
} catch (ClassNotFoundException e) {

//...

//执行javassistCompier

return doCompile(className, code);
}

}

        在javassistCompier编译中,会调用javassist类库编译不同部位的代码最终得到一个完成的Class对象。具体步骤如下:

1、初始化javassit,设置默认参数,如设置当前的classpath。

2、通过正则匹配出所有import的包,并使用javassist添加import。

3、通过正则匹配出所有extends的包,创建Class对象,并使用javassist添加extends。

4、通过正则匹配出所有implements的包,并使用javassist添加implements。

5、通过正则匹配出类里面所有的内容,即得到{}中的内容,再通过正则匹配出所有方法,并使用javassist添加类方法。

6、生成Class对象。完成动态编译。

注意点:

    javassistCompiler继承了AbstractCompiler抽象类,先执行父类compiler时,由于动态类不存在java源码中,所以会报ClassNotFound异常,异常信息被捕获后,会执行子类中的doCompiler方法。完成动态编译。


三、compiler编译器总结
    Compiler接口实现结构图

    获取Compiler实现类时,由于AdaptiveCompiler类存在@Adaptive注解,则默认会使用AdaptiveCompiler作为实现类,在AdaptiveCompier的compiler方法中会控制采用何种编译方式,若设置了自定义编译方式则从缓存中取编译实现类,否则获取默认编译实现类即JavassistCompiler进行动态编译。

    另外可以通过配置来修改使用JdkCompiler进行动态编译。如下图所示:

<dubbo:application name="application" compiler="jdk" />


·  jdkCompiler

    JdkCompiler使用了jdk自带的编译器。通过JavaFileObject、JavaCompiler、JavaFileManager三个接口来进行动态编译,过程简单总结如下:

    1、首先初始化一个JavaFileObject对象,并把字符串作为参数传入构造方法。

    2、调用JavaCompiler.CompilationTask方法编译出具体的类。

    3、JavaFileManager负责管理类文件的输入/输出位置。

以上是关于dubbo动态编译compiler的主要内容,如果未能解决你的问题,请参考以下文章

是否可以动态编译和执行 C# 代码片段?

Dubbo#编译动态扩展类

编译型语言解释型语言静态类型语言动态类型语言概念与区别

编译型语言解释型语言静态类型语言动态类型语言概念与区别

编译型语言解释型语言静态类型语言动态类型语言概念与区别

编译型语言解释型语言静态类型语言动态类型语言概念与区别