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来进行动态代理