找到spring中基于jdk动态代理源码入口即JdkDynamicAopProxy#getProxy方法,其中调用了Proxy.newProxyInstance方法,接下来着重分析该部分源码;
1)Proxy.newProxyInstance(target.getClass.getClassLoader(),target.getClass.getInterfaces(),this);参数一代表被代理类的类加载器,参数二代表代理类需要实现的接口,参数三代表InvocationHandler类型;
2)在newProxyInstance方法中,分三步,第一步是执行getProxyClass0(loader,intfs)方法得到代理类的class类型,intfs表示代理类需要实现的接口;第二步根据代理类的class类型得到构造方法,选择参数为invocationHandler的构造方法;第三步执行构造器的newInstance方法得到代理对象,且其内部持有InvocationHandler,由于InvocationHandler内部有target,所以代理对象也有了target;
3)接着详解getProxyClass0(loader,intfs)方法,该方法中只有一个方法proxyClassCache.get(loader,intfs),这里涉及到weakCache知识点,当用weakCache去get时,若没有get到值,则会执行weakCache构造方法两个作为入参的函数,分别生成key,value存入weakCache中,现在proxyClassCache是一个WeakCache类型,当get返回null时,此时会找到new WeakCache(new KeyFactory(),new ProxyClassFactory()),会分别执行new KeyFactory(),new ProxyClassFactory(),这里重点研究new ProxyClassFactory();
4)ProxyClassFactory类中,定义了代理类型名称前缀为 $ Proxy加唯一数字,接着apply方法实现了创建代理对象的逻辑;该方法中,首先加载所有接口到jvm,并且检查所有接口是否是接口,不是抽象类,没有重复接口;接着生成代理类的包名,访问修饰符;检查接口的访问权限是否为public,若存在不是的,则生成的代理类class就必须和接口在同一个包下,否则会出现访问问题,如果有两个接口,修饰符均不为public,且不在同一个包里,则会报错throw new IllegalArgumentException(“non-public interfaces from different packages”);若包名没赋值,则生成com.sun.proxy作为默认包名;接着根据包名+$Proxy+唯一数字得到代理名字;接着执行 ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)生成二进制字节码,并写入到文件内,它即编译好的字节码文件,接着使用加载器加载二进制字节码文件,即执行defineClass0方法,返回代理类class,此时apply方法执行结束;接着会根据接口,生成代理类的class类型;
5)接着拿到class类型的构造方法,执行newInstance创建代理类实例,入参为invocationHandler;
6)生成的代理类的具体是什么样的呢,将上述字节码文件反编译得到class文件,通过分析该class文件可知,代理类继承了Proxy,实现了被代理接口,所以基于jdk动态代理实现基础是被实现类必须是接口;文件中还有几个static method,是通过Class.forName(“”).getMethod(“方法名”)得到的;有一个有参构造器参数为InvocationHandler类型,代理类实现了被代理接口后,在重写的方法中调用了super.h.invoke(this,m3…),这里的super.h指的是Proxy类中的InvocationHandler类型的实例变量,JdkDynamicAopProxy实现了InvocationHandler接口,所以super.h.invoke(this,m3…)此处调用就是JdkDynamicAopProxy#invoke,在该方法中写增强逻辑,其中利用反射调用被代理类中的相应方法;