引言
动态代理非常的重要,虽然我们在日常的工作中没有非常底层的 编写过动态代理的代码,但是动态代理却起着非常重要的功能,想一下我们经常使用的框架: 日志框架、AOP等等,所以,如果我们需要看一些框架的源码的时候,还是需要非常熟悉的掌握动态代理的原理。
基本的使用这里就不在介绍了,前面的的博客已经介绍过了《java动态代理》,本篇博客主要解决下面几个问题:
1、JDK动态代理基本原理
2、如何调用到我们自己的myInvoerCationHandler中的invoke方法
3、 被代理的类位为什么要实现接口?
例子
package com.jack.springmvc.proxy.JDK; public interface MapperI { void jdbcOperate(); } class Mapper implements MapperI{ @Override public void jdbcOperate() { System.out.println("执行数据库连接操作"); } }
package com.jack.springmvc.proxy.JDK; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInovercationHandler implements InvocationHandler { private Object target; public MyInovercationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("数据库连接前"); //执行目标对象的目标方法 method.invoke(target,args); System.out.println("数据库连接后"); return null; } }
package com.jack.springmvc.proxy.JDK; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) throws Exception { Mapper mapper = new Mapper(); MyInovercationHandler myInovercationHandler = new MyInovercationHandler(mapper); MapperI o = (MapperI)Proxy.newProxyInstance(Mapper.class.getClassLoader(), Mapper.class.getInterfaces(), myInovercationHandler); o.jdbcOperate(); System.out.println(o.getClass()); // ProxyClassUtils.createClassFile(MapperI.class); } }
1、基本原理
我们运行上面的测试方法,得到的是MapperI的一个代理对象,class com.sun.proxy.$Proxy0,执行o.jdbcOperate();这一句代码的基本流程,其实就是上图中的执行流程。为什么会按照和这个流程执行代码呢,在后面我们将生成的代理类的内容输出到文件中,我们打开看看这个类的内容就完全明白了。
2、源码执行流程
3、代理类的内容
我们都知道动态代理生成的代理里我们在项目中看不到的,因为它保存中内存中,所以我们需要编写代码输出到代码内存中。我们从源代码中能看到下面代码:
if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
最后 一个方法中已经得到了代理类的字节数组,所以我们可以模拟这个方法将生成的代理内输入到文件中。
import java.io.FileOutputStream; public class ProxyClassUtils { public static void createClassFile(Class clazz) throws Exception{ String className="$Proxy0"; byte[] classFile = ProxyGenerator.generateProxyClass( className, new Class[]{clazz}); String path ="C:\\project\\luban\\springmvc\\src\\main\\java\\com\\jack\\springmvc\\proxy\\JDK"; FileOutputStream out = new FileOutputStream(path + className + ".class"); out.write(classFile); out.flush(); out.close(); } }
开头的代码中 ProxyClassUtils.createClassFile(MapperI.class) 就可以将内容输出到文件中。代理类内容如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.jack.springmvc.proxy.JDK.MapperI; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements MapperI { private static Method m1; private static Method m2; private static Method m0; private static Method m3; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void jdbcOperate() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m3 = Class.forName("com.jack.springmvc.proxy.JDK.MapperI").getMethod("jdbcOperate"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
我们看到了生成的代理类的内容,我们就能明白非常多的事情了
1、我们看到了生成的代理类继承了Proxy,但是java是单继承的机制,所以如果代理类和被代理类之间产生关联, 只能通过实现同一个接口的方式,这就是为什么,jdk代理要求被代理类必须实现接口。
2、我们还看到了一个核心的方法 jdbcOperate(),这个方法就是我们被代理类实现的接口中的方法,所以我们生成的代理类可以调用和被代理类同一个方法。
这个方法中有一个核心的代码:super.h.invoke(this, m3, (Object[])null); 这里面有几个参数:
1、h.invoke()中的 h 是什么呢,我们从源码中可以看到,cons.newInstance(new Object[]{h}); h就是我们传入的自己定义的 MyInovercationHandler ,看到这个参数我们也就 明白了,为什么我们调用生成的 代理类的jdbcOperate()方法会进入到我们自己定义的MyInovercationHandler 中。
2、m3 我们从代理类的末端可以看到m3 其实就是 我们接口中的方法,看到这我们也就能明白在我们自己的定义的MyInovercationHandler中通过method.invoke(target,args)可以调用目标对象的目标方法了。
分析到这我们也就充分的解答了 开头我们提出的几个问题, 重要的是我们将动态生成的代理类从内存中拿到本地自己看一看,就会明白了很多原来不是很懂的地方,这也是看源码的快乐。