首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dubbo(二)动态编译compiler

dubbo(二)动态编译compiler

作者头像
虞大大
发布2020-09-24 15:31:51
9020
发布2020-09-24 15:31:51
举报
文章被收录于专栏:码云大作战码云大作战

一、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负责管理类文件的输入/输出位置。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码云大作战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档