在看dubbo源码和mybatis源码的时候发现代理用的是javassist, 简单研究下。可以动态的修改一个类,也可以动态的创建类,也可以实现代理(可以基于继承和接口两种)。
pom如下;
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency>
1. 基本用法
1. 实现动态的创建类和增加字段和方法
package org.example.javassit; import javassist.*; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URISyntaxException; public class Test2 { public static void main(String[] args) throws Exception { test2(); } // 动态的添加字段信息 private static void test2() throws Exception { //创建类,这是一个单例对象 ClassPool cp = ClassPool.getDefault(); //我们需要构建的类 CtClass ctClass = cp.get("cn.qz.Person"); //创建字段,指定了字段类型、字段名称、字段所属的类 CtField field = new CtField(cp.get("java.lang.Integer"), "age", ctClass); //指定该字段使用private修饰 field.setModifiers(Modifier.PRIVATE); //设置age字段的getter/setter方法 ctClass.addMethod(CtNewMethod.setter("getAge", field)); ctClass.addMethod(CtNewMethod.getter("setAge", field)); //当前工程的target目录 final String targetClassPath = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath(); //生成.class文件 ctClass.writeFile(targetClassPath); } // 创建类信息 private static void test1() throws CannotCompileException, NotFoundException, URISyntaxException, IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //创建类,这是一个单例对象 ClassPool pool = ClassPool.getDefault(); //我们需要构建的类 CtClass ctClass = pool.makeClass("cn.qz.Person"); //新增字段 CtField field$name = new CtField(pool.get("java.lang.String"), "name", ctClass); //设置访问级别 field$name.setModifiers(Modifier.PRIVATE); //也可以给个初始值 ctClass.addField(field$name, CtField.Initializer.constant("qz-default")); //生成get/set方法 ctClass.addMethod(CtNewMethod.setter("setName", field$name)); ctClass.addMethod(CtNewMethod.getter("getName", field$name)); //新增构造函数 //无参构造函数 CtConstructor cons$noParams = new CtConstructor(new CtClass[]{}, ctClass); cons$noParams.setBody("{name = "qz";}"); ctClass.addConstructor(cons$noParams); //有参构造函数 CtConstructor cons$oneParams = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass); // $0=this $1,$2,$3... 代表方法参数 cons$oneParams.setBody("{$0.name = $1;}"); ctClass.addConstructor(cons$oneParams); // 创建一个名为 print 的方法,无参数,无返回值,输出name值 CtMethod ctMethod = new CtMethod(CtClass.voidType, "print", new CtClass[]{}, ctClass); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("{System.out.println(name);}"); ctClass.addMethod(ctMethod); //当前工程的target目录 final String targetClassPath = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath(); //生成.class文件 ctClass.writeFile(targetClassPath); // 获取Class 对象的两种方式 // 1. 直接转 Class aClass = ctClass.toClass(); // 2. 调用类加载获取 class 信息 // Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("cn.qz.Person"); Object o = aClass.newInstance(); Method method = o.getClass().getMethod("print"); method.invoke(o); } }
2. 通过创建代理类实现增强:
(1) 接口:
package org.example.javassit.proxy; public interface IHelloService { String sayHello(String name); }
(2) 代理接口
package org.example.javassit.proxy; public interface IProxy { void setProxy(Object t); }
(3) 测试类:
package org.example.javassit.proxy; import javassist.*; import java.util.Arrays; /** * @author 乔利强 * @date 2021/8/31 10:45 * @description */ public class ProxyTest { public static void main(String[] args) throws Exception { //创建类,这是一个单例对象 ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath()); //我们需要构建的类 CtClass ctClass = pool.makeClass("org.example.javassit.proxy.HelloServiceJavassistProxy"); //这个类实现了哪些接口 ctClass.setInterfaces(new CtClass[]{ pool.getCtClass("org.example.javassit.proxy.IHelloService"), pool.getCtClass("org.example.javassit.proxy.IProxy")}); //新增字段 CtField field$name = new CtField(pool.get("org.example.javassit.proxy.IHelloService"), "helloService", ctClass); //设置访问级别 field$name.setModifiers(Modifier.PRIVATE); ctClass.addField(field$name); //新增构造函数 //无参构造函数 CtConstructor cons$noParams = new CtConstructor(new CtClass[]{}, ctClass); cons$noParams.setBody("{}"); ctClass.addConstructor(cons$noParams); //重写sayHello方方法,可以通过构造字符串的形式 CtMethod m = CtNewMethod.make(buildSayHello(), ctClass); ctClass.addMethod(m); // 创建一个名为 setProxy 的方法 CtMethod ctMethod = new CtMethod(CtClass.voidType, "setProxy", new CtClass[]{pool.getCtClass("java.lang.Object")}, ctClass); ctMethod.setModifiers(Modifier.PUBLIC); // // $0=this $1,$2,$3... 代表方法参数 ctMethod.setBody("{$0.helloService = $1;}"); ctClass.addMethod(ctMethod); // 写到本地 ctClass.writeFile(Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath()); //获取实例对象 final Object instance = ctClass.toClass().newInstance(); System.out.println(Arrays.toString(instance.getClass().getDeclaredMethods())); //设置目标方法 if (instance instanceof IProxy) { IProxy proxy = (IProxy) instance; proxy.setProxy(new IHelloService() { @Override public String sayHello(String name) { System.out.println("目标接口实现:name=" + name); return "name:" + name; } }); } if (instance instanceof IHelloService) { IHelloService service = (IHelloService) instance; service.sayHello("qz"); } } private static String buildSayHello() { String methodString = " public String sayHello(String name) { " + " System.out.println("静态代理前 .."); " + " helloService.sayHello(name); " + " System.out.println("静态代理后 .."); " + " return name; " + " }"; return methodString; } }
结果:
[public java.lang.String org.example.javassit.proxy.HelloServiceJavassistProxy.sayHello(java.lang.String), public void org.example.javassit.proxy.HelloServiceJavassistProxy.setProxy(java.lang.Object)] 静态代理前 .. 目标接口实现:name=qz 静态代理后 ..
(4) 查看生成的类:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.example.javassit.proxy; public class HelloServiceJavassistProxy implements IHelloService, IProxy { private IHelloService helloService; public HelloServiceJavassistProxy() { } public String sayHello(String var1) { System.out.println("静态代理前 .."); this.helloService.sayHello(var1); System.out.println("静态代理后 .."); return var1; } public void setProxy(Object var1) { this.helloService = (IHelloService)var1; } }
2. 实现代理
1. 基于继承实现
1. 需要增强的类:
package org.example.javassit.proxy2; public class UserDao { public void saveUser() { System.out.println("saveUser ======-"); } }
2. ProxyFactory 实现增强(基于继承实现增强)
package org.example.javassit.proxy2; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import java.lang.reflect.Method; public class ProxyTest { public static void main(String[] args) throws Exception { ProxyFactory factory = new ProxyFactory(); // 设置写出的目录会导出到具体的目录 // factory.writeDirectory = "D:/proxy"; // 指定父类,ProxyFactory会动态生成继承该父类的子类 factory.setSuperclass(UserDao.class); // 设定接口,接口可以继承多个,所以用数组 // factory.setInterfaces(new Class[]{}); //设置过滤器,判断哪些方法调用需要被拦截 factory.setFilter(new MethodFilter() { @Override public boolean isHandled(Method method) { if (method.getName().equals("saveUser")) { return true; } return false; } }); //设置拦截处理 factory.setHandler(new MethodHandler() { @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { System.out.println("前置处理"); Object result = proceed.invoke(self, args); System.out.println("执行结果:" + result); System.out.println("后置处理"); return result; } }); // 创建 UserDao 代理类,并创建代理对象 Class<?> c = factory.createClass(); UserDao javassistTest = (UserDao) c.newInstance(); // saveUser方法,会被拦截 javassistTest.saveUser(); System.out.println(javassistTest.toString()); } }
结果:
前置处理 saveUser ======- 执行结果:null 后置处理 org.example.javassit.proxy2.UserDao_$$_jvst840_0@1593948d
3. 反编译查看类
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.example.javassit.proxy2; import java.io.ObjectStreamException; import java.lang.reflect.Method; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyObject; import javassist.util.proxy.RuntimeSupport; public class UserDao_$$_jvst840_0 extends UserDao implements ProxyObject { public static MethodHandler default_interceptor; private MethodHandler handler; public static byte[] _filter_signature; public static final long serialVersionUID; private static Method[] _methods_; public UserDao_$$_jvst840_0() { this.handler = default_interceptor; if (default_interceptor == null) { this.handler = RuntimeSupport.default_interceptor; } super(); } public final void _d7saveUser() { super.saveUser(); } public final void saveUser() { Method[] var1 = _methods_; this.handler.invoke(this, var1[14], var1[15], new Object[0]); } static { Method[] var0 = new Method[24]; Class var1 = Class.forName("org.example.javassit.proxy2.UserDao_$$_jvst840_0"); RuntimeSupport.find2Methods(var1, "saveUser", "_d7saveUser", 14, "()V", var0); _methods_ = var0; serialVersionUID = -1L; } public void setHandler(MethodHandler var1) { this.handler = var1; } public MethodHandler getHandler() { return this.handler; } Object writeReplace() throws ObjectStreamException { return RuntimeSupport.makeSerializedProxy(this); } }
2. 基于接口
(1) UserDao 接口
package org.example.javassit.proxy2; public interface UserDao { void saveUser(); }
(2) 测试类:
package org.example.javassit.proxy2; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import java.lang.reflect.Method; import java.util.Arrays; public class ProxyTest { public static void main(String[] args) throws Exception { ProxyFactory factory = new ProxyFactory(); // 设置写出的目录会导出到具体的目录 factory.writeDirectory = "D:/proxy"; // 设定接口,接口可以继承多个,所以用数组 factory.setInterfaces(new Class[]{UserDao.class}); //设置过滤器,判断哪些方法调用需要被拦截 factory.setFilter(new MethodFilter() { @Override public boolean isHandled(Method method) { if (method.getName().equals("saveUser")) { return true; } return false; } }); //设置拦截处理 factory.setHandler(new MethodHandler() { @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { System.out.println("前置处理"); System.out.println("self: " + self + " thisMethod: " + thisMethod + " proceed: " + proceed + " args: " + Arrays.toString(args)); System.out.println("后置处理"); return ""; } }); // 创建 UserDao 代理类,并创建代理对象 Class<?> c = factory.createClass(); UserDao javassistTest = (UserDao) c.newInstance(); // saveUser方法,会被拦截 javassistTest.saveUser(); System.out.println(javassistTest.toString()); } }
(3) 结果;
前置处理 self: org.example.javassit.proxy2.UserDao_$$_jvst840_0@1b604f19 thisMethod: public abstract void org.example.javassit.proxy2.UserDao.saveUser() proceed: null args: [] 后置处理 org.example.javassit.proxy2.UserDao_$$_jvst840_0@1b604f19
(4) 反编译查看类
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.example.javassit.proxy2; import java.io.ObjectStreamException; import java.lang.reflect.Method; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyObject; import javassist.util.proxy.RuntimeSupport; public class UserDao_$$_jvst840_0 implements UserDao, ProxyObject { public static MethodHandler default_interceptor; private MethodHandler handler; public static byte[] _filter_signature; public static final long serialVersionUID; private static Method[] _methods_; public UserDao_$$_jvst840_0() { this.handler = default_interceptor; if (default_interceptor == null) { this.handler = RuntimeSupport.default_interceptor; } super(); } public final void saveUser() { Method[] var1 = _methods_; this.handler.invoke(this, var1[14], var1[15], new Object[0]); } static { Method[] var0 = new Method[24]; Class var1 = Class.forName("org.example.javassit.proxy2.UserDao_$$_jvst840_0"); RuntimeSupport.find2Methods(var1, "saveUser", (String)null, 14, "()V", var0); _methods_ = var0; serialVersionUID = -1L; } public void setHandler(MethodHandler var1) { this.handler = var1; } public MethodHandler getHandler() { return this.handler; } Object writeReplace() throws ObjectStreamException { return RuntimeSupport.makeSerializedProxy(this); } }