小A同学想要找女朋友,自己又不好意思,所以需要一个媒婆帮助他找对象,媒婆作为小A的代理,下面我们使用cglib动态代理,来用代码实现下:
xiaoA:
public class XiaoA {
public void findLove(){
System.out.println("我是小A,帮我找个女朋友");
}
}
meipo:
public class Meipo implements MethodInterceptor{
public Object getInstance(Class clazz) throws Exception{
//输出class文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//返回代理类对象
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("我是媒婆:");
System.out.println("------------");
proxy.invokeSuper(obj, args);
System.out.println("------------");
System.out.println("寻找中....找到啦");
return null;
}
}
测试类:
public class TestCglibProxy {
public static void main(String[] args) {
try {
// 代理类class文件存入本地磁盘方便我们反编译查看源码 D:/$Proxy0.class
XiaoA obj = (XiaoA)new Meipo().getInstance(XiaoA.class);
System.out.println(obj.getClass());
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印结果:
是不是很神奇,下面我们通过源码来深入分析下。
从上面的可以看出,代理类对象是由Enhancer创建的。Enhancer是CGLIB的字节码增强器,可以·很方便的对类进行扩展,如上面的为生成的类设置父类。
创建代理对象会经过三步:
1.生成代理类的二进制字节码文件。
2.加载二进制字节码文件到JVM,生成class对象。
3.反射获得实例构造方法,创建代理对象。
下面我们来看下代理类class文件反编译后的代码:
现在,我们将代理类的class文件反编译一下(截取部分代码):
public class 173894b8 extends XiaoA
implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0; //拦截器
private static final Method CGLIB$findLove$0$Method; //被代理的方法
private static final MethodProxy CGLIB$findLove$0$Proxy; //代理方法
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1()
{
Class localClass2; //被代理类
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("com.bzy.proxy.cglib.XiaoA$$EnhancerByCGLIB$$173894b8"); //代理类
Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = tmp95_92[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
Method[] tmp115_95 = tmp95_92;
CGLIB$equals$2$Method = tmp115_95[1];
CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
Method[] tmp135_115 = tmp115_95;
CGLIB$toString$3$Method = tmp135_115[2];
CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
Method[] tmp155_135 = tmp135_115;
CGLIB$hashCode$4$Method = tmp155_135[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
Method[] tmp175_155 = tmp155_135;
CGLIB$clone$5$Method = tmp175_155[4];
CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
tmp175_155;
Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "findLove", "()V" }, (localClass2 = Class.forName("com.bzy.proxy.cglib.XiaoA")).getDeclaredMethods());
CGLIB$findLove$0$Method = tmp223_220[0];
CGLIB$findLove$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "findLove", "CGLIB$findLove$0");
tmp223_220;
return;
}
}
通过代理类源码可以看到,代理类会获得所有在父类继承类的方法,并且有MethodProxy与之对应,例如CGLIB$findLove$0$Method与CGLIB$findLove$0$Proxy
//代理方法(methodProxy.invokeSuper会调用)
final void CGLIB$findLove$0()
{
super.findLove();
}
//被代理方法(methodProxy.invoke会调用),这就是为什么在拦截器中调用methodProxy.invoke会死循环,因为下面的代码中一直在调用intercept
public final void findLove()
{
//this.CGLIB$CALLBACK_0==null
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
//首先执行CGLIB$BIND_CALLBACKS
if (tmp4_1 == null)
{
CGLIB$BIND_CALLBACKS(this);
tmp4_1 = this.CGLIB$CALLBACK_0;
}
//再执行下面
if (this.CGLIB$CALLBACK_0!= null)
//调用拦截器,this就是当前的代理类
tmp4_1.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs,CGLIB$findLove$0$Proxy);
}
至此,调用的过程为:代理对象调用this.findlove->调用拦截器(intercept)->执行拦截器中的前面代码->执行invokeSuper(会执行CGLIB$findLove$0方法)->调用父类findLove->执行拦截器后面代码
相信大家这里会有疑问,他是如何通过invokeSuper调用到CGLIB$findLove$0方法的呢?
首先在调用intercept时,将CGLIB$findLove$0$Proxy传入到了intercept,然后下面我们分析下如何通过CGLIB$findLove$0$Proxy.invokeSuper调用到CGLIB$findLove$0方法的。
在拦截器中,通过调用MethodProxy的invokeSuper方法来调用代理方法,我们来分析下MethodProxy看下他到底做了些什么?
首先我们看下MethodProxy的创建:
下图是上面代理类class中findLove的代理方法的创建,参数从左到右依次是:
被代理对象,代理对象,入参类型,被代理方法名,代理方法名
我们再看下MethodProxy.create的源码与类中的属性:
private Signature sig1; //相当于实体类的作用
private Signature sig2;
private MethodProxy.CreateInfo createInfo; //也可以认为是实体类
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo; //没有被初始化
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc); //将被代理对象与方法入参传入
proxy.sig2 = new Signature(name2, desc); //将代理对象与方法入参传入
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);//将被代理对象与代理对象传入
return proxy;
}
这里我们不深究代码,这里其实就可以认为是创建了一个MethodProxy对象,将代理对象,代理方法,被代理对象,被代理方法都传进去了。
下面我们再来看下invokeSuper的源码:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//fastCLass.invoke(代理方法索引,代理对象,方法参数)
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
首先调用了init方法:
private void init() {
//首先会执行这个分支,因为create时并没有给他赋值
if (this.fastClassInfo == null) {
//拿到上面的final对象作为锁
Object var1 = this.initLock;
synchronized(this.initLock) {
//true
if (this.fastClassInfo == null) {
//this.createInfo包含了代理类与被代理类的信息
MethodProxy.CreateInfo ci = this.createInfo;
//创建新的FastClassInfo对象
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1); //获得被代理对象的FastClass,如果缓存中有就从缓存中取出,没有就生成新的fastclass
fci.f2 = helper(ci, ci.c2); //获得代理对象的FastClass,如果缓存中有就从缓存中取出,没有就生成新的fastclass
fci.i1=fci.f1.getIndex(this.sig1); //获得被代理对象中被代理方法的索引
fci.i2 = fci.f2.getIndex(this.sig2);//获得代理对象中代理方法的索引
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
//FastClassInfo 构造方法
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
五、FastClass机制
Cglib动态代理执行代理方法效率之所以比JDK高是因为Cglib采用了FastClass机制,他为代理类和被代理类各生成了一个class,这个class会为代理类与被代理类的方法分类index。这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。
FastClass不是与代理类一起生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放入缓存。
这里就不贴代码展示了,因为fastClass反编译后的代码有点乱,并且好多错误。
我们来从头总结下MethodProxy调用流程:
首先main方法中调用getInstance调用Enhancer创建代理类class文件,并将代理类引用返回。
然后调用代理对象.findLove()
然后通过反编译代理类,查看findLove方法,发现在findLove中会调用拦截器方法
然后在拦截器方法中会通过MethodProxy调用invokeSuper方法
调用invokeSuper方法为什么能调用到代理类的CGLIB$findLove$0?
下面看下MethodProxy的流程
create:在代理类中create一个MethodProxy,将被代理对象与方法传入Signature1,将代理对象与方法传入Signature2,将代理类对象与被代理类对象传入 MethodProxy.CreateInfo,Signature1与MethodProxy.CreateInfo可以认为是实体类存储信息的。
调用methodProxy.invokeSuper了,调用invokeSuper时又会首先调用init方法。
init方法就是给在create方法中没有赋值的fastclassInfo赋值,将createInfo与Signature1与Signature2中的信息取出,转成fastclass,如果fastclass在缓存中有就从缓存中取,没有的话就生成新的fastclass
通过Signature1与Signature2获得方法的索引,存入i1与i2
通过fci.f2.invoke(fci.i2, obj, args) fastclass反射调用代理对象的代理方法的索引,直接定位到代理方法。
代理方法中调用super.findLove()