Dubbo源码之动态编译

简介: Dubbo源码之动态编译

Dubbo里的代理类

JavassistProxyFactory:利用字节码技术来创建对象

public <T> T getProxy(Invoker<T> invoker,Class<?>[] interfaces) {
       return (T) Proxy.getProxy(interfaces).newInstance(newInvokerInvocationHndler(invoker));
}

看似跟jdk生成代理一样, 其实这里的Proxy类不是jdk中自带那个生成代理对象的类是com.alibaba.dubbo.common.bytecode.Proxy。

这个dubbo自己写的Proxy类,利用要代理的接口利用javassist工具生成代理代码。

 

什么是Javassist

package com.soa.other.compiler;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Modifier;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
/**
 * Javassist是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建 Java类。
 * 以下例子就是创建一个动态类
 * 
 */
public class CompilerByJavassist {
  public static void main(String[] args) throws Exception {
    // ClassPool:CtClass对象的容器
    ClassPool pool = ClassPool.getDefault();
    // 通过ClassPool生成一个public新类Emp.java
    CtClass ctClass = pool.makeClass("com.study.javassist.Emp");
    // 添加属性
    // 首先添加属性private String ename
    CtField enameField = new CtField(pool.getCtClass("java.lang.String"),
        "ename", ctClass);
    enameField.setModifiers(Modifier.PRIVATE);
    ctClass.addField(enameField);
    // 其次添加熟悉privtae int eno
    CtField enoField = new CtField(pool.getCtClass("int"), "eno", ctClass);
    enoField.setModifiers(Modifier.PRIVATE);
    ctClass.addField(enoField);
    // 为属性ename和eno添加getXXX和setXXX方法
    ctClass.addMethod(CtNewMethod.getter("getEname", enameField));
    ctClass.addMethod(CtNewMethod.setter("setEname", enameField));
    ctClass.addMethod(CtNewMethod.getter("getEno", enoField));
    ctClass.addMethod(CtNewMethod.setter("setEno", enoField));
    // 添加构造函数
    CtConstructor ctConstructor = new CtConstructor(new CtClass[] {},
        ctClass);
    // 为构造函数设置函数体
    StringBuffer buffer = new StringBuffer();
    buffer.append("{\n").append("ename=\"yy\";\n").append("eno=001;\n}");
    ctConstructor.setBody(buffer.toString());
    // 把构造函数添加到新的类中
    ctClass.addConstructor(ctConstructor);
    // 添加自定义方法
    CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo",
        new CtClass[] {}, ctClass);
    // 为自定义方法设置修饰符
    ctMethod.setModifiers(Modifier.PUBLIC);
    // 为自定义方法设置函数体
    StringBuffer buffer2 = new StringBuffer();
    buffer2.append("{\nSystem.out.println(\"begin!\");\n")
        .append("System.out.println(ename);\n")
        .append("System.out.println(eno);\n")
        .append("System.out.println(\"over!\");\n").append("}");
    ctMethod.setBody(buffer2.toString());
    ctClass.addMethod(ctMethod);
    //最好生成一个class
    Class<?> clazz = ctClass.toClass();
    Object obj = clazz.newInstance();
    //反射 执行方法
    obj.getClass().getMethod("printInfo", new Class[] {})
        .invoke(obj, new Object[] {});
    // 把生成的class文件写入文件
    byte[] byteArr = ctClass.toBytecode();
    FileOutputStream fos = new FileOutputStream(new File("D://Emp.class"));
    fos.write(byteArr);
    fos.close();
  }
}
package com.soa.other.compiler;
public class Emp {  
    private String ename;  
    private int eno;  
    public Emp(){  
        ename="yy";  
        eno=001;  
    }  
    public String getEname() {  
        return ename;  
    }  
    public void setEname(String ename) {  
        this.ename = ename;  
    }  
    public int getEno() {  
        return eno;  
    }  
    public void setEno(int eno) {  
        this.eno = eno;  
    }  
    //添加一个自定义方法  
    public void printInfo(){  
        System.out.println("begin!");  
        System.out.println(ename);  
        System.out.println(eno);  
        System.out.println("over!");  
    }  
}  

运行结果:

Dubbo如何用Javassist实现动态代理

package com.alibaba.dubbo.common.compiler;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("javassist")
public interface Compiler {
    /**
     * Compile java source code.
     *
     * @param code        Java source code
     * @param classLoader TODO
     * @return Compiled class
     */
    Class<?> compile(String code, ClassLoader classLoader);
}

从接口定义代码我们可以看到dubbo通过SPI机制使用了Javasist

SPI机制,(在java.util.ServiceLoader里有比较详细的介绍)简单来说就是为某个接口寻找服务实现的机制

动态编译实现的类图

package com.alibaba.dubbo.common.compiler.support;
import com.alibaba.dubbo.common.utils.ClassHelper;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * JavassistCompiler. (SPI, Singleton, ThreadSafe)
 *
 * @author william.liangf
 */
public class JavassistCompiler extends AbstractCompiler {
    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]+;");
    @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());
    }
}

为什么没有使用CGLib,而使用Javassist

这里引用网上的一个测试结论:

1. ASM和JAVAASSIST字节码生成方式不相上下,都很快,是CGLIB的5倍。

2. CGLIB次之,是JDK自带的两倍。

3. JDK自带的再次之,因JDK1.6对动态代理做了优化,如果用低版本JDK更慢,要注意的是JDK也是通过字节码生成来实现动态代理的,而不是反射。

4. JAVAASSIST提供者动态代理接口最慢,比JDK自带的还慢

最终选型: Javassist

虽然ASM稍快,但并没有快一个数量级,

而JAVAASSIST的字节码生成方式比ASM方便,

JAVAASSIST只需用字符串拼接出Java源码,便可生成相应字节码,

而ASM需要手工写字节码。

怎么配置动态代理方式

<dubbo:provider proxy="jdk" />或<dubbo:consumer proxy="jdk" />  默认情况下使用Javassist来进行动态代理


相关文章
|
6月前
|
缓存 Dubbo Java
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
|
6月前
|
Dubbo Java 应用服务中间件
从源码全面解析 dubbo 服务端服务调用的来龙去脉
从源码全面解析 dubbo 服务端服务调用的来龙去脉
|
6月前
|
缓存 Dubbo Java
Dubbo 第三节_ Dubbo的可扩展机制SPI源码解析
Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。Protocol是⼀个接。但是,不是只要在⽅法上加了。
|
6月前
|
Dubbo Java 应用服务中间件
微服务框架(十七)Dubbo协议及编码过程源码解析
  此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。   本文为Dubbo协议、线程模型、和其基于Netty的NIO异步通讯机制及源码
|
6月前
|
缓存 负载均衡 Dubbo
从源码全面解析 dubbo 消费端服务调用的来龙去脉
从源码全面解析 dubbo 消费端服务调用的来龙去脉
|
6月前
|
存储 Dubbo Java
从源码全面解析 dubbo 服务订阅的来龙去脉
从源码全面解析 dubbo 服务订阅的来龙去脉
|
6月前
|
Dubbo 数据可视化 Java
从源码全面解析 dubbo 服务暴露的来龙去脉
从源码全面解析 dubbo 服务暴露的来龙去脉
|
6月前
|
Dubbo Java 应用服务中间件
从源码全面解析 dubbo 注解配置的来龙去脉
从源码全面解析 dubbo 注解配置的来龙去脉
|
存储 Dubbo Java
Dubbo第三讲:Dubbo的可扩展机制SPI源码解析
Dubbo第三讲:Dubbo的可扩展机制SPI源码解析
|
负载均衡 Dubbo 算法
Dubbo的Adaptive自适应机制原理与源码深度解析
Dubbo的Adaptive自适应机制原理与源码深度解析
177 0
下一篇
无影云桌面