Java的动态代理模式之Cglib代理详解
作者:四叶草
这篇文章主要介绍了Java的动态代理模式之Cglib代理详解,Cglib代理也叫作 子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理,需要的朋友可以参考下
1.Cglib 代理模式的基本介绍
- 静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个 单独的对象,并没 有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 Cglib 代理
- Cglib代理也叫作 子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
- Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 SpringAOP,实现方法拦截
- 在 AOP 编程中如何选择代理模式:
目标对象需要实现接口,用 JDK 代理
目标对象不需要实现接口,用 Cglib 代理
Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
2.Cglib 代理模式实现步骤
需要引入 cglib 的 jar 文件
/* cglib包结构: net.sf.cglib.core 底层字节码处理类。 net.sf.cglib.transform 该包中的类用于class文件运行时转换或编译时转换。 net.sf.cglib.proxy 该包中的类用于创建代理和方法拦截。 net.sf.cglib.reflect 该包中的类用于快速反射,并提供了C#风格的委托。 net.sf.cglib.util 集合排序工具类。 net.sf.cglib.beans JavaBean工具类。 */
在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
3.cglib动态代理相关的基础类
net.sf.cglib.proxy.Enhancer 主要的增强类。
net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。
cglib是通过动态的生成一个子类去覆盖所要代理类的非final方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(intercept)
4.Cglib 代理模式应用实例
4.1 导入pom.xml依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
4.2 目标类
//火车站 public class TrainStation { public void sell() { System.out.println("火车站卖票"); } }
4.3 代理工厂
//代理工厂 public class ProxyFactory implements MethodInterceptor { //声明目标对象 private TrainStation target = new TrainStation(); public TrainStation getProxyObject() { //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数 Enhancer enhancer =new Enhancer(); //设置代理对象的父类的字节码对象(Class类型的对象) , 指定代理对象的父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 , 实现调用代理对象的方法时最终都会执行MethodInterceptor的子实现类的intercept方法 , 在这个函数中利用反射完成任意目标类方法的调用 enhancer.setCallback(this); //设置完参数后就可以 ,默认返回的是Object类型 , 可以进行强转 , 创建真正的代理对象 TrainStation obj = (TrainStation) enhancer.create(); return obj; } // intercept方法参数说明: // 返回值类型是调用方法的返回值类型 // o : 代理对象 // method : 真实对象中的方法的Method实例对象 // args : 实际参数 , 可以是0到N个 // methodProxy :代理对象中的方法的method实例 public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)"); //调用目标对象的方法 TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args); return result; } }
Enhancer类的常用方法
方法名 | 功能 |
public Enhancer() , 无参构造 | 创建Enhancer对象,类似于JDK动态代理的Proxy类,并不是真正的代理对象 , 还没有设置参数 |
setSuperclass() | 设置代理对象的父类的字节码对象(Class类型的对象) , 指定代理对象的父类 , 参数一般写目标类的Class对象 |
setCallback() | 设置执行哪个对象的回调函数 , 调用代理对象的方法时最终都会执行MethodInterceptor接口的子实现类对象的intercept方法 , 在intercept方法中利用反射完成任意目标类方法的调用 一般让代理工厂实现MethodInterceptor接口 , 那么方法参数就可以写this,这时MethodInterceptor接口的子实现类对象就是代理工厂对象 |
public Object create() | 创建真正的代理对象 , 默认返回的是Object类型 , 可以强转为目标对象类型 , 创建真正的代理对象 |
4.4 测试类
//测试类 public class Client { public static void main(String[] args) { //创建代理工厂对象 ProxyFactory factory = new ProxyFactory(); //获取代理对象 TrainStation proxyObject = factory.getProxyObject(); proxyObject.sell(); } }
5 jdk代理和CGLIB代理
- 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。
- 唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。final修饰类不能被继承 , final修饰的方法不能被重写
- 在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
6 代理模式优缺点及使用场景
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
- 增加了系统的复杂度;
使用场景:
- 远程(Remote)代理
- 本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。RPC思想
- 防火墙(Firewall)代理
- 当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。
- 保护(Protect or Access)代理
- 务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。RPC思想
- 防火墙(Firewall)代理
- 当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。
- 保护(Protect or Access)代理
- 控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
到此这篇关于Java的动态代理模式之Cglib代理详解的文章就介绍到这了,更多相关Cglib代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!