1、JDK静态代理
1 | package org.jphoebe.edu.demo.proxy.staticProxy; |
2、JDK动态代理
1 | package org.jphoebe.edu.demo.proxy.dynamic; |
这样就可以解释Mybatis为什么定义一个接口就可以执行sql
1 | package org.jphoebe.edu.demo.proxy.dynamic; |
动态代理生成的类
1 | package org.jphoebe.edu.demo.proxy.dynamic; |
可以看出,生成的类继承了被代理对象的接口,所以JDK原生的动态代理是基于接口的,如果对象没有使用接口,那么就无法使用jdk动态代理,需要使用cglib动态代理
2、cglib动态代理
1 | package org.jphoebe.edu.demo.proxy.cglib; |
使用cglib代理,会生成
1 | CglibUser$$EnhancerByCGLIB$$45809d07$$FastClassByCGLIB$$db8bd788.class |
三个文件
EnhancerByCGLIB:是生成的代理对象
FastClassByCGLIB:是原对象方法的索引地址(避免使用了反射,而是直接调用对象方法)
EnhancerByCGLIB$$45809d07$$FastClassByCGLIB:是生成代理对方法的索引地址
fastclass中的部分代理
1 | public int getIndex(Signature var1) { |
生成class源码:
1 | // MethodInterceptor |
生成对象核心代码
1 | public void generateClass(ClassVisitor v) { |
再来看看正常java代码的字节码
1 | // class version 52.0 (52) |
会发现有诸多相似的命令,如:
1 | dup (dup()) |
等等
所以CGlib实际上是直接写字节码来创建代理类,所以也就无所无是否继承了接口,代理类只需要直接代理这个类就行。
总结
¶jdk动态代理
¶特点
Interface:对于JDK Proxy,业务类是需要一个Interface的,这是一个缺陷;
Proxy:Proxy类是动态产生的,这个类在调用Proxy.newProxyInstance()方法之后,产生一个Proxy类的实力。实际上,这个Proxy类也是存在的,不仅仅是类的实例,这个Proxy类可以保存在硬盘上;
Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来。
InvocationHandler:这个类在业务委托类执行时,会先调用invoke方法。invoke方法在执行想要的代理操作,可以实现对业务方法的再包装。
JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()
¶cglib动态代理
¶特点
- CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
- 对指定的目标生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
- 注意:jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。
¶性能对比
关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。主要体现在如下的两个指标中:
- CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
- 但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
- 对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。