注解动态设置参数_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字节码编译方式。

dcbfc41b96dcba8355e94900804baa74.png

·  AdaptiveCompiler.compile

public Class> compile(String code, ClassLoader classLoader) {
Compiler compiler; ExtensionLoader 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接口实现结构图

2d7324c52e295adadfcaad557eed9a31.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值