深度解析dubbo动态编译技术

注:本文dubbo版本v2.6.1

1.前言

我们在介绍dubbo spi技术的时候曾讲到过getAdaptiveExtension获取自适应扩展实现类没有实现类上面添加@Adaptive dubbo就会自动创建代码,然后编译,也就是这段代码

 private Class<?> createAdaptiveExtensionClass() {

        // 拼装代理类的java代码
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        // 进行编译
        return compiler.compile(code, classLoader);
    }

第一行是拼接java代码,第二行是获取classloader,第三行就是获取Compiler扩展点的自适应实现类,第四行就是返回编译的实现类class。本篇我们就来看下Compiler 这个扩展点。

2. Compiler

@SPI("javassist")
public interface Compiler {

    /**
     * Compile java source code.
     *
     * @param code        Java source code
     * @param classLoader classloader
     * @return Compiled class
     */
    Class<?> compile(String code, ClassLoader classLoader);

}

我们可以看到,Compiler这个扩展点默认实现是javassist,然后一个compile抽象方法,参数分别是代码和classloader,返回一个class对象。
在当前dubbo版本中该接口一共有3个实现类,分别是

  1. AdaptiveCompiler 自适应Compiler ,类上面带有@Adaptive注解。
  2. JavassistCompiler 这个实现类是Javassist技术来做的。
  3. JdkCompiler 使用jdk自带的技术来实现的。
    其中后面两个实现类都是继承AbstractCompiler 抽象类。分别实现类doCompile方法。
    接下来我们就按照这个顺序来分别看看他们是怎样实现的。

3. AdaptiveCompiler 实现原理

@Adaptive
public class AdaptiveCompiler implements Compiler {

    private static volatile String DEFAULT_COMPILER;

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

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

}

我们可以看到它有个静态成员DEFAULT_COMPILER,表示默认Compiler,还有set方法可以设置这个值。
然后我们看下它的compile方法
首先是获取Compiler的ExtensionLoader对象loader,然后判断DEFAULT_COMPILER有没有设置值,如果有的话就使用loader 来获取扩展点实现类对象,没有就用就获取默认的扩展点对象,这里默认的就是JavassistCompiler。然后再调用扩展点实现对象的compile方法。

4.AbstractCompiler抽象类

AbstractCompiler是个抽象类,实现Compiler接口,然后JavassistCompiler,JdkCompiler这个两个类又分别继承AbstractCompiler,实现doCompile方法,接下来我们来看下源码。

public abstract class AbstractCompiler implements Compiler {

    private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");

    private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");

    @Override
    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 {
            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 {
                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;

}

有两个成员PACKAGE_PATTERN,CLASS_PATTERN分别是获取package 与class的 正则。
我们来看下他是怎么实现Compiler的compile(String code, ClassLoader classLoader)方法的,
先用PACKAGE_PATTERN 来找到code里面的包名,CLASS_PATTERN 找到code的类名,用包名+类名,就是code的全类名className,用Class.forName()方法看看之前加载没有,没有有直接ruturn,没有的话,判断code是不是“}”结尾的,然后再调用doCompile(String name, String source)方法,doCompile是个抽象类,由子类实现。

5. JavassistCompiler实现原理

我们先来看下JavassistCompiler类的类成员

   private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");

    private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");

    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]+;");

可以看出定义了一堆正则,有import,extends,implements还有方法成员变量的。
接着我们再来看下doCompile(String name, String source)方法。

@Override
    public Class<?> doCompile(String name, String source) throws Throwable {
        int i = name.lastIndexOf('.');
        String className = i < 0 ? name : name.substring(i + 1);
        ClassPool pool = new ClassPool(true);
        pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));
        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);
                }
            }
        }
        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 = 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.addInterface(pool.get(ifaceClass));
            }
        }
        String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
        String[] methods = METHODS_PATTERN.split(body);
        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));
                }
            }
        }
        return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
    }

首先是截取到类名,然后创建ClassPool对象,接着就是截取到import,然后调用importPackage方法设置import。
接着就是截取extends与implements,再接着就是处理method与field,最后就是toClass了。
其实这里就是将传过来java代码用正则切割,然后利用javassist自己的处理方式设置进去。
对javassist不了解的可以看我另一篇文章:javassist入门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

$码出未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值