在Dubbo中,大量使用动态代理相关技术。动态代理主要是基于JDK的动态代理和Javassist的动态代理。
有关于JDK动态代理的使用及源码解析可以参考上文。
本文着重来介绍下Javassist的使用,及其动态代理的实现。
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。(来自百度百科)
通过这个介绍,我们了解到javassist可以操作java字节码,而通过操作字节码,还能实现动态代理。
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.25.0-GA</version> </dependency>
package xw.demo.proxy.javassist; public class Student { private String name; private int age; // 省略get set方法 public String study(String book) { return name + "study " + book; } }
2.3.1 ClassPool加载已知类
ClassPool pool = ClassPool.getDefault(); // 加载Student CtClass studentClass = pool.get("xw.demo.proxy.javassist.Student");
2.3.2 CtField获取属性
CtField nameField = studentClass.getField("name"); // 获取属性类型,本例中即为java.lang.String String nameType = nameField.getType().getName(); // 获取属性注解 Object[] annotations = nameField.getAnnotations(); ...
2.3.3 CTMethod获取方法信息
CtMethod method = studentClass.getDeclaredMethod("study"); // 获取方法返回类型 CtClass returnType = method.getReturnType(); // 获取方法参数类型 CtClass[] parameterTypes = method.getParameterTypes();
2.3.4 CtConstructor获取构造方法信息
CtConstructor[] declaredConstructors = studentClass.getDeclaredConstructors(); if (null != declaredConstructors) { for (CtConstructor ctConstuctor : declaredConstructors) { // 获取构造方法名称 String name = ctConstuctor.getName(); // 获取构造方法参数类型 CtClass[] ctConstuctorParameterTypes = ctConstuctor.getParameterTypes(); } }
javassist的使用还是比较简单的。但这只是开胃菜,真正重要的是其用来动态创建一个新的类。下面我们就用Javassist来创建一个新的Student类,与上述有一样的属性和方法。
2.3.5 javassist创建新的类
public static void newClassTest() { ClassPool pool = ClassPool.getDefault(); // 创建class信息 CtClass studentClass = pool.makeClass("xw.demo.proxy.javassist.Student_copy"); try { // 创建name属性 CtField nameField = new CtField(pool.get("java.lang.String"), "name", studentClass); nameField.setModifiers(Modifier.PUBLIC); studentClass.addField(nameField); // 创建age属性 CtField ageField = new CtField(CtClass.intType, "age", studentClass); ageField.setModifiers(Modifier.PUBLIC); studentClass.addField(ageField); CtMethod studyMethod = new CtMethod(pool.get("java.lang.String"), "study", new CtClass[]{pool.get("java.lang.String")}, studentClass); studyMethod.setModifiers(Modifier.PUBLIC); studyMethod.setBody("return name + \"study \" + $1;"); studentClass.addMethod(studyMethod); // 写出到文件 studentClass.writeFile("D:\\test"); } catch (CannotCompileException | IOException e) { e.printStackTrace(); } catch (NotFoundException e) { e.printStackTrace(); } }
最终会生成一个Student_copy.class,具体信息如下
package xw.demo.proxy.javassist; public class Student_copy { public String name; public int age; public String study(String paramString) { return this.name + "study " + paramString; } }
之前有写过通过JDK proxy来实现动态代理的文章,使用JDK自带的还是比较方便的。那么当我们使用Javassist该如何实现动态代理呢,实际也是很简单的。直接看示例。
2.4.1 创建示例方法
// 创建被代理类 public class EchoService { public String echo(String msg) { return "echo " + msg; } } public static void proyTest() { ClassPool pool = new ClassPool(); try { CtClass echoServiceClass = pool.get("xw.demo.proxy.javassist.EchoService"); // 获取其echo方法,并在方法前后添加执行语句 CtMethod echoMethod = echoServiceClass.getDeclaredMethod("echo"); echoMethod.insertBefore("before echo..."); echoMethod.insertAfter("after echo..."); // 执行echo方法 EchoService echoService = (EchoService)echoServiceClass.toClass().newInstance(); String world = echoService.echo("world"); System.out.println(world); } catch (NotFoundException | CannotCompileException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } // 执行结果: before echo... echo world after echo...
Javassist通过对字节码的执行,在方法执行的前后添加自定义代码,就可以实现对方法的动态代理。
2.4.2 Dubbo中Javassist动态代理的使用
Dubbo使用Javassist来实现动态代理,代码如下:
public class JavassistProxyFactory extends AbstractProxyFactory { @Override @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { // 具体可见getProxy()方法 return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } }
具体细节笔者就不再展示,也是通过动态生成一个Proxy类,来实现对invoker的动态代理的。