动态代理
动态代理原理
用到的基本知识 : 反射,字节码,(一点)类加载机制
好处
静态代理需要挨个实现要代理的接口和函数,动态代理只需要实现一个InvocationHandler
接口,剩下的交给自动生成的代理类和反射。举例如图 ,基本的使用简单不贴代码。注:图中的Proxy.newProxyInstance()
就是获取一个自动生成的代理类对象
JDK动态代理基本流程
代理对象产生的流程
基本原理:给Proxy类传入我们要代理的类的接口的class
和ClassLoader
,前者主要用于遍历所有函数生成字节码,后者主要用于加载字节码进JVM。还要传入我们实现的Invocation
接口,用于给自动生成的代理类调用。
总体函数调用流程,过程中涉及的缓存机制后面写
代理对象实现代理的流程
举个自动生成代理类中的例子(反编译c生成的lass文件)
//自动生成的动态代理类
public final void update() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
关键的一句super.h.invoke(this, m3, (Object[])null);
此处调用的就是我们自己在BookServiceProxy
写的:
//BookServiceProxy.java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args); // 调用 target 的 method 方法
after();
return result; // 返回方法的执行结果
}
而m3
则是在该类的静态代码块中赋值的m3 = Class.forName("proxy.BookService").getMethod("update");
由此总结:要代理多个方法,其实还是要写多个方法,只是这个过程由自动生成这个步骤代替了。之后用反射的方式在我们实现的invoke
中执行不同的函数的目的。
Proxy类中的缓存 : WeakCache
基本思路
核心思想就是从缓存中拿,没有就创建。源码看起来多/难懂是因为有好多代码用于处理多线程的同步和检查由于弱引用而被GC的key。用弱引用大概是实现缓存的核心,因为弱引用的对象会被GC,我知道节省内存,但目前不太清楚具体节省在了何处。
用了一个Map套Map的结构实现二级缓存,但目前不明白做二级缓存的原因。
外层Map(一级缓存)的key使用弱引用,由ClassLoader生成,内存Map(二级缓存)的key使用强引用,由ClassLoader和interfaces生成,他的value就是一个用于生成字节码的Factory
对象或者CacheValue
即包装了字节码的一个对象。这两个都实现了Supplier
接口。其中Factory
最终调用Proxy
中传过来的valueFactory.apply()
生成字节码
整体长这样
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
另外还有一些私有的成员用于生成Key(subKeyFactory),生成Value(valueFactory),快速查看Value是否存在(reverseMap),不贴代码。
CGLib动态代理基本流程
cglib将目标类的非final
的并且public
的方法定义转成字节码,并最终用于生成class对象,该class对象继承于目标类。不同于jdk的是,cglib还会给代理类和被代理类都生成一个fastclass(调用的时候才会生成,后面讲)
使用的时候只需要自己实现MethodInterceptor
接口,用于存放代理逻辑,还可以实现CallbackFilter
用于选择某个方法使用哪个代理类。
public static void main(String[] args) throws IOException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
enhancer.setCallbacks(new Callback[]{new My Interceptor1(),new MyInterceptor2()});
enhancer.setCallbackFilter(new MyFilter()); //过滤器,用于根据方法选择使用哪个回调
TargetObject proxyObj = (TargetObject) enhancer.create();
proxyObj.method1("我是IKun");
proxyObj.method2(114514);
}
public class MyFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if (method.getName().equals("method1")) {
return 0; // Callback 列表第1个拦截器
}
return 1; // Callback 列表第2个拦截器,return 2 则为第3个,以此类推
}
}
为什么CGlib运行效率比Jdk高
关键就是cglib的代理类使用fastclass为方法建立索引实现调用方法不用反射。
java反射调用方法是比较“重”的操作,要经过一系列的权限验证、通过native方法请求jvm去方法区查找方法定义、以及最后的invoke仍然可能要通过JNI调用native方法。
如何实现方法索引?
看源码就知道了,这是被代理类的fastclass
,这个invoke
是被MethodProxy.invoke()
调用的。
从这也可以看见其实最后调用函数是在fastclass
中发生的。后面有整个调用流程图
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
TargetObject var10000 = (TargetObject)var2;
int var10001 = var1;
try {
switch (var10001) {
case 0:
return var10000.toString();
case 1:
return var10000.method1((String)var3[0]);
case 2:
return new Integer(var10000.method3(((Number)var3[0]).intValue()));
case 3:
return new Integer(var10000.method2(((Number)var3[0]).intValue()));
case 4:
return new Boolean(var10000.equals(var3[0]));
case 5:
return new Integer(var10000.hashCode());
}
......
[尚未理解]为什么要为代理和被代理类各生成一个fastclass??
暂时不理解为什么给代理类也生成一个fastclass
,因为如果 MethodProxy.invoke
会调用代理类的函数,这个函数又会调用MethodInterceptor.intercept()
,会无限递归,这样看的话,代理类的fastclass
就没有存在的必要了。
而且,我发现生成的代理类class反编译之后,有代理之后的方法,也有父类的方法(如下CGLIB$method1$1
),这就更没必要生成两个了。。。
final String CGLIB$method1$1(String var1) {
return super.method1(var1);
}
public final String method1(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$method1$1$Method, new Object[]{var1}, CGLIB$method1$1$Proxy) : super.method1(var1);
}
调用流程
本文作者:DL
本文链接:https://www.cnblogs.com/BayMax0-0/p/17734372.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步