dubbo源码解析(4) dubbo的动态编译

56 篇文章 2 订阅

前面几节我们分析了dubbo的spi、ioc和aop,这一篇我们探究dubbo的最后一个内核,动态编译

在分析动态编译前,先介绍下javassitst

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

Javassist中最为重要的是ClassPool,CtClass ,CtMethod 以及 CtField这几个类。

ClassPool:一个基于HashMap实现的CtClass对象容器,其中键是类名称,值是表示该类的CtClass对象。默认的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节。

CtClass:表示一个类,这些CtClass对象可以从ClassPool获得。

CtMethods:表示类中的方法。

CtFields :表示类中的字段

下面一段代码会生成一个top.ss007.GenerateClass

     ClassPool pool = ClassPool.getDefault();
     CtClass ct = pool.makeClass("top.ss007.GenerateClass");//创建类
     ct.setInterfaces(new CtClass[]{pool.makeInterface("java.lang.Cloneable")});//让类实现Cloneable接口
     try {
         CtField f= new CtField(CtClass.intType,"id",ct);//获得一个类型为int,名称为id的字段
         f.setModifiers(AccessFlag.PUBLIC);//将字段设置为public
         ct.addField(f);//将字段设置到类上
         //添加构造函数
         CtConstructor constructor=CtNewConstructor.make("public GeneratedClass(int pId){this.id=pId;}",ct);
         ct.addConstructor(constructor);
         //添加方法
         CtMethod helloM=CtNewMethod.make("public void hello(String des){ System.out.println(des);}",ct);
         ct.addMethod(helloM);

         ct.writeFile();//将生成的.class文件保存到磁盘

         //下面的代码为验证代码
         Field[] fields = ct.toClass().getFields();
         System.out.println("属性名称:" + fields[0].getName() + "  属性类型:" + fields[0].getType());
     } catch (CannotCompileException e) {
         e.printStackTrace();
     } catch (IOException e) {
         e.printStackTrace();
     } catch (NotFoundException e) {
         e.printStackTrace();
     }
 }

生成的GenerateClass
public class GenerateClass implements GenerateInterface {
    public int id;

    public GenerateClass(int var1) {
        this.id = var1;
    }

    public void hello(String var1) {
        System.out.println(var1);
    }
}

javassist同样可以实现aop

假设有这么一个类

public class Point {
    private int x;
    private int y;

    public Point(){}
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void move(int dx, int dy) {
        this.x += dx;
        this.y += dy;
    }
}
我们要动态的在内存中在move()方法体的前后插入一些代码

    public void modifyMethod()
    {
        ClassPool pool=ClassPool.getDefault();
        try {
            CtClass ct=pool.getCtClass("top.ss007.Point");
            CtMethod m=ct.getDeclaredMethod("move");
            m.insertBefore("{ System.out.print(\"dx:\"+$1); System.out.println(\"dy:\"+$2);}");
            m.insertAfter("{System.out.println(this.x); System.out.println(this.y);}");

            ct.writeFile();
            //通过反射调用方法,查看结果
            Class pc=ct.toClass();
            Method move= pc.getMethod("move",new Class[]{int.class,int.class});
            Constructor<?> con=pc.getConstructor(new Class[]{int.class,int.class});
            move.invoke(con.newInstance(1,2),1,2);
        }
        ...
    }
最终move方法会变成

 public void move(int dx, int dy) {
    System.out.print("dx:" + dx);System.out.println("dy:" + dy);
    this.x += dx;
    this.y += dy;
    Object localObject = null;//方法返回值
    System.out.println(this.x);System.out.println(this.y);
  }
关于更多javassist方面的知识,可以参考官网 http://www.javassist.org/ 这里就不细说了

------------------------------------------------------

言归正传,说完了javassist

找到Extensionloader#createAdaptiveExtensionClass()

private Class<?> createAdaptiveExtensionClass() {
    String code = this.createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    Compiler compiler = (Compiler)getExtensionLoader(Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

第一句话我们之前已经分析了就是获得一段代码字符串,第二句话是获取类加载器

重点看这句话

Compiler compiler = (Compiler)getExtensionLoader(Compiler.class).getAdaptiveExtension();

Compiler的实现类中是有@Adaptive注解的一个实现类,就是AdaptiveCompiler,在dubbo的配置文件中信息是这样的

adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

所以最终的compiler是AdaptiveCompiler

进入AdaptiveCompiler

@Adaptive
public class AdaptiveCompiler implements Compiler {
    private static volatile String DEFAULT_COMPILER;

    public AdaptiveCompiler() {
    }

    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

    public Class<?> compile(String code, ClassLoader classLoader) {
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        String name = DEFAULT_COMPILER;
        Compiler compiler;
        if (name != null && name.length() > 0) {
            compiler = (Compiler)loader.getExtension(name);
        } else {
            compiler = (Compiler)loader.getDefaultExtension();
        }

        return compiler.compile(code, classLoader);
    }
}

ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); 

获取到的loader为

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

会根据AdaptiveCompiler的DEFAULT_COMPILER是否会空来走,因为之前injectExtension并没有注入DEFAULT_COMPILER的值,所以走下面 compiler = (Compiler)loader.getDefaultExtension();

getDefaultExtension()会根据@SPI的value值来创建一个Extension

return null != this.cachedDefaultName && this.cachedDefaultName.length() != 0 && !"true".equals(this.cachedDefaultName) ? this.getExtension(this.cachedDefaultName) : null;

Compiler上@SPI上的value为@SPI("javassist")

所以compiler为JavassistCompiler

实际上AdaptiveCompiler就是一个装饰类,实际被装饰的对象就是JavassistCompiler或者别的

JavassistCompiler的compiler方法在AbstractCompiler中

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);
    if (!matcher.find()) {
        throw new IllegalArgumentException("No such class name in " + code);
    } else {
        String cls = matcher.group(1);
        String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;

        try {
            return Class.forName(className, true, ClassHelper.getCallerClassLoader(this.getClass()));
        } catch (ClassNotFoundException var11) {
            if (!code.endsWith("}")) {
                throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");
            } else {
                try {
                    return this.doCompile(className, code);
                } catch (RuntimeException var9) {
                    throw var9;
                } catch (Throwable var10) {
                    throw new IllegalStateException("Failed to compile class, cause: " + var10.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(var10));
                }
            }
        }
    }
}

return Class.forName(className, true, ClassHelper.getCallerClassLoader(this.getClass()));

这句话会根据className去加载这个class,但是由于xxx$Adaptive事先在路径中没有文件,所以会报错

进入catch

doCompile(className, code);
JavassistCompiler#doCompile

比较复杂,归纳一下就是利用Javassist编译出class,具体原理我们已经在一开始说Javassist的时候已经说明了

这样xxxx$Adaptive的类就被动态编译出来了

 

本篇结束。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值