Java动态代理、CGLIB动态代理
文章目录
- 代理模式
- 静态代理
- 动态代理
- CGLIB动态代理
- JDK动态代理源码分析
代理模式
代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
根据代理类的创建时间又可以分为:
- 静态代理
- 动态代理
两者的区别:
- 静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
- 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
但是不管是静态代理还是动态代理,他们都有一个共同点就是:都是通过接口进行代理。委托方通过接口去寻找代理方,而代理方也是通过去实现接口来完成代理对象的创建。并且代理方都会在方法中通过引用去调用委托方。
静态代理
某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
静态代理的特点
目标角色固定
在应用程序执行前就得到目标角色
代理对象会增强目标对象的行为
有可能存在多个代理 引起"类爆炸"(缺点)
针对静态代理使用以下的例子:
- 委托方为People类
- 代理接口为ProxyInterface
- 静态代理类为StaticProxy
接下来我们直接看代码:
ProxyInterface代理接口:
public interface ProxyInterface {String sing();
}
委托方People:
public class People implements ProxyInterface {private String name;public People(String name) {this.name = name;}public String sing(){System.out.println(name + "正在唱歌");return name+"表示感谢";}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
静态代理方StaticProxy
public class StaticProxy implements ProxyInterface{//委托方引用private People people;public StaticProxy(People people) {this.people = people;}public String sing() {System.out.println("这里是代理对People的增强");return people.sing();}
}
然后我们测试一下:
public class ProxyTest {public static void main(String[] args) {StaticProxy zsProxy = new StaticProxy(new People("张三"));System.out.println(zsProxy.sing());}
}
结果:
我们可以知道:
静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。
动态代理
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。
动态代理的特点
目标对象不固定
在应用程序执行时动态创建目标对象
代理对象会增强目标对象的行为
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring AOP的原理。
针对动态代理我们使用如下的例子:
- 代理接口ProxyInterface
- 委托方People
- 代理方使用Proxy.newProxyInstance方法动态生成
- InvocationHandler回调处理程序的实现类DynamicProxy
其中代理接口ProxyInterface和委托方People的代码与前面一样,DynamicProxy的代码如下:
public class DynamicProxy<T> implements InvocationHandler {private final T target;public DynamicProxy(T target) {this.target = target;}/*** proxy:代表动态代理对象* method:代表正在执行的方法* args:代表调用method时传入的实参*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("sing")) {System.out.println("这里是对sing方法动态代理的增强");}return method.invoke(target,args);}
}
测试代码:
public class ProxyTest {public static void main(String[] args) {// StaticProxy zsProxy = new StaticProxy(new People("张三"));
// System.out.println(zsProxy.sing());People zs = new People("张三");ProxyInterface proxyInterface = (ProxyInterface) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),new Class[]{ProxyInterface.class},new DynamicProxy<People>(zs));System.out.println(proxyInterface.sing());}
}
理解:
我们的InvocationHandler中涉及代理对象的引用和invoke方法,我们使用newProxyInstance创建代理对象的时候,传入代理接口就是为了对接口里面的方法进行拦截,然后全部替换成执行invoke方法。
结果:
CGLIB动态代理
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,从而可以快速的查找对应的方法代理,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。
在底层实现上,CGLIB 使用字节码处理框架 ASM,该框架用于转换字节码并生成新的类。但是不鼓励直接使用 ASM,因为它要求对于 JVM 的内部结构包括 class 文件的格式和 JVM 指令都很熟悉,若一旦出现错误,会导致 JVM 崩溃级别的异常。
虽然说 CGLIB 与 JDK 动态代理相比,具有更好的优势,当并不是说使用 CGLIB 会更好,这里先总结下 CGLIB 和 JDK 动态代理之间的区别:
- JDK 动态代理只能对接口进行代理,不能对普通的类进行代理,这是因为 - JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承。
- CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。
- JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
- CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效。
我们接下来看一个使用的例子:
首先我们导入相关依赖包:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>
我们的委托方还是前面的Person类对象张三,代码如下:
public class CglibTry {public static void main(String[] args) {final People zs = new People("张三");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(People.class);enhancer.setCallback(new MethodInterceptor() {/*** @param o: 代理对象* @param method: 被代理方法* @param params: 方法入参* @param methodProxy: CGLIB方法**/public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if (method.getName().equals("sing")) {System.out.println("这里是cglib对sing方法的动态增强");}return method.invoke(zs,objects);}});People cglibProxy = (People) enhancer.create();System.out.println(cglibProxy.sing());}
}
结果:
当然cglib也可以代理接口,我们尝试一下:
public class CglibProxyInterfaceTry {public static void main(String[] args) {final People zs = new People("张三");Enhancer enhancer = new Enhancer();enhancer.setInterfaces(new Class[]{ProxyInterface.class});enhancer.setCallback(new MethodInterceptor() {public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if (method.getName().equals("sing")) {System.out.println("这里是cglib对sing方法的动态增强");}return method.invoke(zs,objects);}});ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();System.out.println(proxyInterface.sing());}
}
区别在于:
- 最后使用enhancer.create()创建的对象是代理接口类型的,而不是委托方的类型
- 接口代理使用的是setInterfaces方法而不是setSuperclass方法(当然这个不绝对,我们可以看看下面的代码)
public class CglibProxyInterfaceTry {public static void main(String[] args) {// final People zs = new People("张三");
// Enhancer enhancer = new Enhancer();
// enhancer.setInterfaces(new Class[]{ProxyInterface.class});
// enhancer.setCallback(new MethodInterceptor() {// public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// if (method.getName().equals("sing")) {// System.out.println("这里是cglib对sing方法的动态增强");
// }
// return method.invoke(zs,objects);
// }
// });
// ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();
// System.out.println(proxyInterface.sing());final People zs = new People("张三");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(ProxyInterface.class);enhancer.setCallback(new MethodInterceptor() {public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if (method.getName().equals("sing")) {System.out.println("这里是cglib对sing方法的动态增强");}return method.invoke(zs,objects);}});ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();System.out.println(proxyInterface.sing());}
}
也就是说setSuperclass代理接口也是可以的。
JDK动态代理源码分析
我们直接来看newProxyInstance方法:
此处得到安全管理器非重点,继续往下看,我们可以看到一个getProxyClass0方法:
这个方法非常重要,就是通过它来生成的代理类。
拿到这个代理类之后,通过这个代理类的构造方法并传入InvocationHandler对象来得到一个代理对象。
接着我们来看看代理类的产生方法getProxyClass0:
我们可以看到这个地方使用了缓存。如果由实现给定接口的给定加载器定义的代理类存在,则只返回缓存副本;否则,它将通过ProxyClassFactory创建代理类
未完待续
Java动态代理、CGLIB动态代理相关推荐
- 深入理解Java Proxy和CGLIB动态代理原理
点击上方关注,每天进步一点点 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译 ...
- Java Proxy和CGLIB动态代理原理
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
- java jdk动态代理 cglib动态代理demo
最近在研究java动态代理这块,以前也看了很多次java动态代理,感觉一直不是怎么明白,这两天看了看又明白了些,现给出我参考网上写的一个demo jdk动态代理实现: View Code import ...
- Java设计模式(五)代理设计模式—静态代理—JDK动态代理—Cglib动态代理
文章目录 什么是代理模式 代理模式应用场景 代理的分类 静态代理 什么是静态代理 深入解析静态代理 小结 动态代理 什么是动态代理 JDK动态代理 原理和实现方式 代码实现 优缺点 Cglib动态代理 ...
- 静态代理,cglib动态代理,jdk动态代理区别以及流程详解
1.静态代理 静态代理使用的是代理设计模式,不讲高大上的思想,我们直接实战 这是动物接口,其中有一个吃饭方法 这是其中的一只动物,实现了动物接口,覆盖了吃饭方法 现在我们思考,我想要给猫找一个代理,希 ...
- 动态代理——CGLIB动态代理原理示例解析
觉得可以的话点个关注,转个发呗,陆续奉上干货~~~~ 前文我们讲解了JDK动态代理的原理(动态代理--JDK动态代理原理),今天我们来看看CGLIB动态代理是如何实现,最后我们总结下JDK动态代理和C ...
- Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理
Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...
- java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)
java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理 基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...
- Java两种动态代理JDK动态代理和CGLIB动态代理
目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...
- Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)
Java之代理... 1 一. 概念... 1 二. jdk的静态代理... 1 三. jdk动态代理... 4 四. cglib 动态 ...
最新文章
- linux zip 命令详解
- oracle数据库装不成功,oracle 10.2.0.4安装 创建数据库时 em不成功需要大的补丁
- doc es 中type_Elasticsearch(024):es常见的字段映射类型之 连接类型(join type)
- java项目(注册和登录(成功后查看商品的信息))
- networkX如何读取存储图的二进制.dat文件
- 为什么我们不应该使用过多的线程
- ArcGIS API for Silverlight地图加载众多点时,使用Clusterer解决重叠问题
- Android 调用系统相机拍照和录制视频,保存照片和视频
- 如何使用Visual Studio无需成本即可实现连续集成
- 为七牛云存储开发的PHP PEAR 包:Services_Qiniu
- java程序设计基础_陈国君版第五版_第十章习题
- python 中sg表示什么_十分钟带你入门最Python风格的Gui库
- 蓝桥杯每日一练----字符串逆序
- 【精华】安卓开发学习路线规划
- 前端学习之HTML入门
- 如何为你的网站添加标志性的图标(头像)呢?
- 互联网金融数据分析应用
- NOI openjudge 计算2的N次方
- es bulk java_Java Elasticsearch Bulk API 批量操作
- easy_yinkelude 简单的文件包含
热门文章
- Android4.0 SDK功能详解
- LeetCode(89):格雷编码 Gray Code(Java)
- 【操作说明】RTSP/GB28181/EHOME协议视频融合平台EasyCVR如何通过OBS接收RTMP协议推流
- Airbnb上市只是时间问题
- wget下载需登录的网页中的文件
- |Vijos|动态规划|P1264 神秘的咒语
- kettle教程---kettle作业调度,根据更新时间增量更新
- 工业自动化MODBUS协议读写器读卡器配置软件|工具之读写卡模式配置操作攻略
- PC客户端UI自动化
- 【IPv6+燎原系列—第1期】不要以为IPv6+与你无关,它已影响到你的收入