可以对比一下上面发现 UserDaoImpl 发现编译器除了为我们添加了一个公有的构造器,其他基本一致。
经过这个简单的示例后,cxuan 给你演示一下如何使用 Javaassist 动态代理。
首先我们先创建一个 Javaassist 的代理工厂,代码如下
public class JavaassistProxyFactory { public Object getProxy(Class clazz) throws Exception{ // 代理工厂 ProxyFactory proxyFactory = new ProxyFactory(); // 设置需要创建的子类 proxyFactory.setSuperclass(clazz); proxyFactory.setHandler((self, thisMethod, proceed, args) -> { System.out.println("---- 开始拦截 ----"); Object result = proceed.invoke(self, args); System.out.println("---- 结束拦截 ----"); return result; }); return proxyFactory.createClass().newInstance(); } }上面我们定义了一个代理工厂,代理工厂里面创建了一个 handler,在调用目标方法时,Javassist 会回调 MethodHandler 接口方法拦截,来调用真正执行的方法,你可以在拦截方法的前后实现自己的业务逻辑。最后的 proxyFactory.createClass().newInstance() 就是使用字节码技术来创建了最终的子类实例,这种代理方式类似于 JDK 中的 InvocationHandler 接口。
测试方法如下
public static void main(String[] args) throws Exception { JavaassistProxyFactory proxyFactory = new JavaassistProxyFactory(); UserService userProxy = (UserService) proxyFactory.getProxy(UserService.class); userProxy.saveUser(); } ASM 代理ASM 是一套 Java 字节码生成架构,它可以动态生成二进制格式的子类或其它代理类,或者在类被 Java 虚拟机装入内存之前,动态修改类。
下面我们使用 ASM 框架实现一个动态代理,ASM 生成的动态代理
以下代码摘自 https://blog.csdn.net/lightj1996/article/details/107305662
public class AsmProxy extends ClassLoader implements Opcodes { public static void createAsmProxy() throws Exception { // 目标类类名 字节码中类修饰符以 “/” 分割 String targetServiceName = TargetService.class.getName().replace(".", "http://www.likecs.com/"); // 切面类类名 String aspectServiceName = AspectService.class.getName().replace(".", "http://www.likecs.com/"); // 代理类类名 String proxyServiceName = targetServiceName+"Proxy"; // 创建一个 classWriter 它是继承了ClassVisitor ClassWriter classWriter = new ClassWriter(0); // 访问类 指定jdk版本号为1.8, 修饰符为 public,父类是TargetService classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyServiceName, null, targetServiceName, null); // 访问目标类成员变量 为类添加切面属性 “private TargetService targetService” classWriter.visitField(ACC_PRIVATE, "targetService", "L" + targetServiceName+";", null, null); // 访问切面类成员变量 为类添加目标属性 “private AspectService aspectService” classWriter.visitField(ACC_PRIVATE, "aspectService", "L" + aspectServiceName+";", null, null); // 访问默认构造方法 TargetServiceProxy() // 定义函数 修饰符为public 方法名为 <init>, 方法表述符为()V 表示无参数,无返回参数 MethodVisitor initVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); // 从局部变量表取第0个元素 “this” initVisitor.visitVarInsn(ALOAD, 0); // 调用super 的构造方法 invokeSpecial在这里的意思是调用父类方法 initVisitor.visitMethodInsn(INVOKESPECIAL, targetServiceName, "<init>", "()V", false); // 方法返回 initVisitor.visitInsn(RETURN); // 设置最大栈数量,最大局部变量表数量 initVisitor.visitMaxs(1, 1); // 访问结束 initVisitor.visitEnd(); // 创建有参构造方法 TargetServiceProxy(TargetService var1, AspectService var2) // 定义函数 修饰符为public 方法名为 <init>, 方法表述符为(TargetService, AspectService)V 表示无参数,无返回参数 MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(L" + targetServiceName + ";L"+aspectServiceName+";)V", null, null); // 从局部变量表取第0个元素 “this”压入栈顶 methodVisitor.visitVarInsn(ALOAD, 0); // this出栈 , 调用super 的构造方法 invokeSpecial在这里的意思是调用父类方法。 <init>的owner是AspectService, 无参无返回类型 methodVisitor.visitMethodInsn(INVOKESPECIAL, targetServiceName, "<init>", "()V", false); // 从局部变量表取第0个元素 “this”压入栈顶 methodVisitor.visitVarInsn(ALOAD, 0); // 从局部变量表取第1个元素 “targetService”压入栈顶 methodVisitor.visitVarInsn(ALOAD, 1); // this 和 targetService 出栈, 调用targetService put 赋值给this.targetService methodVisitor.visitFieldInsn(PUTFIELD, proxyServiceName, "targetService", "L" + targetServiceName + ";"); // 从局部变量表取第0个元素 “this”压入栈顶 methodVisitor.visitVarInsn(ALOAD, 0); // 从局部变量表取第2个元素 “aspectService”压入栈顶 methodVisitor.visitVarInsn(ALOAD, 2); // this 和 aspectService 出栈 将 targetService put 赋值给this.aspectService methodVisitor.visitFieldInsn(PUTFIELD, proxyServiceName, "aspectService", "L" + aspectServiceName + ";"); // 方法返回 methodVisitor.visitInsn(RETURN); // 设置最大栈数量,最大局部变量表数量 methodVisitor.visitMaxs(2, 3); // 方法返回 methodVisitor.visitEnd(); // 创建代理方法 修饰符为public,方法名为 demoQuest MethodVisitor visitMethod = classWriter.visitMethod(ACC_PUBLIC, "demoQuest", "()I", null, null); // 从局部变量表取第0个元素 “this”压入栈顶 visitMethod.visitVarInsn(ALOAD, 0); // this 出栈 将this.aspectService压入栈顶 visitMethod.visitFieldInsn(GETFIELD, proxyServiceName, "aspectService", "L"+aspectServiceName+";"); // 取栈顶元素出栈 也就是targetService 调用其preOperation方法, demoQuest的owner是AspectService, 无参无返回类型 visitMethod.visitMethodInsn(INVOKEVIRTUAL, aspectServiceName,"preOperation", "()V", false); // 从局部变量表取第0个元素 “this”压入栈顶 visitMethod.visitVarInsn(ALOAD, 0); // this 出栈, 取this.targetService压入栈顶 visitMethod.visitFieldInsn(GETFIELD, proxyServiceName, "targetService", "L"+targetServiceName+";"); // 取栈顶元素出栈 也就是targetService调用其demoQuest方法, demoQuest的owner是TargetService, 无参无返回类型 visitMethod.visitMethodInsn(INVOKEVIRTUAL, targetServiceName, "demoQuest", "()I", false); // 方法返回 visitMethod.visitInsn(IRETURN); // 设置最大栈数量,最大局部变量表数量 visitMethod.visitMaxs(1, 1); // 方法返回 visitMethod.visitEnd(); // 生成字节码二进制流 byte[] code = classWriter.toByteArray(); // 自定义classloader加载类 Class<?> clazz = (new AsmProxy()).defineClass(TargetService.class.getName() + "Proxy", code, 0, code.length); // 取其带参数的构造方法 Constructor constructor = clazz.getConstructor(TargetService.class, AspectService.class); // 使用构造方法实例化对象 Object object = constructor.newInstance(new TargetService(), new AspectService()); // 使用TargetService类型的引用接收这个对象 TargetService targetService; if (!(object instanceof TargetService)) { return; } targetService = (TargetService)object; System.out.println("生成代理类的名称: " + targetService.getClass().getName()); // 调用被代理方法 targetService.demoQuest(); // 这里可以不用写, 但是如果想看最后生成的字节码长什么样子,可以写 "ascp-purchase-app/target/classes/"是我的根目录, 阅读者需要将其替换成自己的 String classPath = "/Users/mr.l/cxuan-justdoit/"; String path = classPath + proxyServiceName + ".class"; FileOutputStream fos = new FileOutputStream(path); fos.write(code); fos.close(); } }