cglib代理和 jdk 动态代理原理查不多
对比一下 CGLIB 代理和 JDK 动态代理的原理
-
回调接口的差异
- JDK 动态代理使用的回调接口是
InvocationHandler
- CGLIB 代理使用的回调接口是
MethodInterceptor
- JDK 动态代理使用的回调接口是
-
调用目标的差异
- jdk, cglib 使用
method.invoke()
进行反射调用
需 要调用足够次数才会进行优化 - cglib使用
methodProxy.invoke(obj, args)
进行不反射调用
这种方式可以直接调用到目标对象的方法,性能相对更好
Spring 框架采用这种方式 - cglib使用
methodProxy.invokeSuper(proxy, args)
进行不反射调用
这种方式可以直接调用到代理对象的方法,可以省略目标对象
- jdk, cglib 使用
cglib代理
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyExample {
public static void main(String[] args) {
Ia proxy = (Ia) Enhancer.create(Ia.class, new MethodInterceptorImpl());
proxy.doSomething();
}
private static class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invocation: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method invocation: " + method.getName());
return result;
}
}
interface Ia {
void doSomething();
}
static class IaImpl implements Ia {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
}
FastClass
public abstract class FastClass {
private Class type;
protected FastClass() {
throw new Error("Using the FastClass empty constructor--please report to the cglib-devel mailing list");
}
protected FastClass(Class type) {
this.type = type;
}
// .....
public abstract int getIndex(String name, Class[] parameterTypes);
public abstract int getIndex(Class[] parameterTypes);
public abstract Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException;
public abstract Object newInstance(int index, Object[] args) throws InvocationTargetException;
public abstract int getIndex(Signature sig);
public abstract int getMaxIndex();
// .....
}
cglib 避免反射调用
-
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类,会实现上述的抽象方法
例如:-
ProxyFastClass 配合代理对象一起使用, 避免反射
-
TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
-
-
TargetFastClass 记录了 Target 中方法与编号的对应关系
假如有如下三个方法:-
save(long) 编号 2
-
save(int) 编号 1
-
save() 编号 0
-
首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号
-
然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射
-
-
ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 ProxyFastClass 额外提供了下面几个方法
-
saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)
-
saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)
-
saveSuper() 编号 0,不增强, 仅是调用 super.save()
-
查找方式与 TargetFastClass 类似
-
-
为什么有这么麻烦的一套东西呢?
-
避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
-
用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
-