我的JDK动态代理流程
我梳理的动态代理流程大约是:
-
如果每一个框架都有自己的BPP,且自己的BPP中都有自己的wrapIfNecessory,那样可能就是一个BPP一个代理类。但通常应该都是各自的框架以提供 Advisior(切面)的方式,让AOP的BPP去处理他们的Advisior。
-
BeanPostProcessor 处理bean的时候,会找到这个bean匹配上的的advisior(切面)的集合。
-
ProxyFactory 拿到adivisor的集合和目标类之后
- 设置当前代理的 advicisor、设置当前代理的目标对象 targetClass,设置当前代理的接口类
- 使用Jdk或者cglib动态代理创建代理类
createAopProxy().getProxy();
-
生成代理类:
-
JDK动态代理
-
JDK动态代理要求被代理类必须实现方法,否则报错。原因当然和JDK动态代理的原理有关系了。
public Object getProxy(@Nullable ClassLoader classLoader) {return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); }
-
生成代理类,
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {。。。byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);。。。 }
-
生成代理类
-
将所有方法包装成ProxyMethod对象
- 将Object类中的hashCode、equals、toString方法包装成ProxyMethod对象
- 获取代理类接口列表
- 遍历每个接口的每个方法,并包装成ProxyMethod对象
-
为代理类组装各种字段信息和方法信息
- 添加构造器方法,该构造器参数为InvocationHandler类型
- 添加静态字段及代理方法
- 添加静态字段的初始化方法
-
写入class文件
- 验证常量池中存在代理类的全限定名
- 验证常量池中存在代理类父类的全限定名,即Proxy类
- 验证常量池中存在代理类接口的全限定名
- 写入class文件前,将常量池设为只读,当前常量池中的变量不允许修改
- 每个class文件的前四个字节为魔数,用来确定这个文件是否是一个能被虚拟机接受的class文件,
- 后面再跟两个字节的次版本号和两个字节的主版本号
- 。。。
- 转成二进制文件输出
-
-
-
-
在调用代理对象方法时,会在方法内执行
super.h.invoke(this, m3, new Object[]{var1});
。即调用JdkDynamicAopProxy的invoke方法。final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {// 这里传的是thispublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());}return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);} }public class Proxy implements java.io.Serializable {// super.h = new JdkDynamicAopProxy();protected InvocationHandler h;public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)}public final String getUserByName(String var1) throws {/*** 调用父类的h属性的invoke方法*/return (String)super.h.invoke(this, m3, new Object[]{var1});} }
-
JdkDynamicAopProxy的invoke方法
-
获取MethodInterceptor集合 chain:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
-
对于拦截器链为空的情况,会直接执行目标方法
-
对于拦截器链不为空的情况,会将代理对象、目标方法、拦截器链等信息,封装为一个 ReflectiveMethodInvocation 对象,然后通过它的
proceed
方法完成拦截器中的增强逻辑和目标方法的执行。-
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
- 调用方法
retVal = invocation.proceed();
-
-