Java动态代理和静态代理详解
目录
- 1.什么是代理?
- 2.为什么要使用代理?
- 3静态代理
- 3.1.静态代理的示例
- 4.动态代理
- 4.1.cglib和jdk动态代理的区别
- 4.2.cglib动态代理示例
- 4.3.JDK动态代理示例
1.什么是代理?
生活中的代理是很常见的,比如代购、律师、中介
等,他们都有一个共性就是帮助被代理人处理一些前前后后的事情
。而被代理人只需要专注做自己要做的那部分事情就可以了
。当然这里的被代理人不可能是只有一个,假如只有一个那根本养不活这群代理。
Java中的代理也是类似的,代理可以帮助被代理者完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由被代理者完成。就好比中介找房,你想要什么房中介都可以帮你找,但是钱还是得你出,房还是得你亲自看。
2.为什么要使用代理?
从生活的角度上来说,租房为什么需要中介呢?我们直接找房东不好吗,其实中介他起到的作用是汇集房源,假如我们直接找房东,先不说房东好不好找,房子肯定是有数的,一个房东顶多也就几套房,而一个中介可能顶上好几个房东的房源了。说直白点,租房是我们的目的,在不改变自己的目的情况下,快速的找到房源,那就是通过中介!
从代码角度上来说,在不修改源码的基础上对方法进行加强。当然不是所有代码涉及到增强就需要使用代理,而是很多方法都涉及到了增强,才会统一使用一个代理
,举例:我们要给所有的接口添加操作日志,这时候不可能说在每个接口上添加操作日志,这样会导致重复代码一大堆,所以这时候我们从中间抽出来一个代理,被代理类只需要专注于自己的核心代码即可,日志记录交给代理类就可以了,优点:使得代码更加简洁,分工明确。
3静态代理
代理模式分为动态代理
和静态代理
。两者的差别还是很大的,不过思想都是一样的,起到一个服务中介的作用。
所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件,而动态代理是通过某种方式来生成的代理字节码文件。
3.1.静态代理的示例
以下是通过一个买车示例来演示静态代理的思想,实际开发当中我们可能不会这么写,但是更多的是需要理解他的思想!
(1)定义接口,接口当中有一个抽象方法
/*** 买车接口* @author guo**/
public interface StaticMy {void lawsuit();
}
(2)定义被代理类,实现StaticMy接口
package com.gzl.static1;/*** 被代理方 我* @author guo**/
public class StaticMyImpl implements StaticMy{public void lawsuit() {System.out.println("我要买车");}}
(3)代理类同样也需要实现StaticMy接口,将被代理类通过构造器的方式传入代理类,由代理类对被代理类进行加强
package com.gzl.static1;public class Shop implements StaticMy{private StaticMyImpl staticMy;public Shop(StaticMyImpl staticMy) {super();this.staticMy = staticMy;}public void lawsuit() {System.out.println("厂子进车");this.staticMy.lawsuit();System.out.println("交车");}}
(4)测试
package com.gzl.static1;public class Test {public static void main(String[] args) {// 创建被代理类StaticMyImpl shop = new StaticMyImpl();// 将被代理类传入代理类当中Shop shop2 = new Shop(shop);// 由代理类来执行shop2.lawsuit();}
}
执行结果
4.动态代理
动态代理就是,在程序运行期,创建目标对象的代理对象
,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作
。
静态代理是直接在代码中声明好的代理对象,而动态代理中的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理一般有两种实现方式,cglib和jdk。
4.1.cglib和jdk动态代理的区别
- JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- CGLIB代理使用字节码处理框架ASM,对代理对象类的class文件加载进来,通过修改字节码生成子类。
- JDK创建代理对象效率较高,执行效率较低;
- CGLIB创建代理对象效率较低,执行效率高。
- JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
- CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。
JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
- 实现InvocationHandler接口,重写invoke()
- 使用Proxy.newProxyInstance()产生代理对象
- 被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
- 实现MethodInterceptor接口,重写intercept()
- 使用Enhancer对象.create()产生代理对象
4.2.cglib动态代理示例
jdk动态代理只能为接口创建代理,使用上有局限性。实际的场景中我们的类不一定有接口,此时如果我们想为普通的类也实现代理功能,我们就需要用到cglib来实现了。
cglib是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口;本质上它是通过动态的生成一个子类去覆盖所要代理的类
(非final修饰的类和方法
)。Enhancer可能是CGLIB中最常用的一个类,和jdk中的Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口
。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法
,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对final类进行代理操作
。
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy和BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。
(1)cglib并不是java当中自带的,所以使用的话需要引入jar包
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.1_3</version></dependency>
</dependencies>
(2)创建一个类,写入两个方法
package com.itheima.cglib;/*** 一个生产者*/
public class Producer {/*** 销售* @param money*/public void saleProduct(float money){System.out.println("销售产品,并拿到钱:"+money);}/*** 售后* @param money*/public void afterService(float money){System.out.println("提供售后服务,并拿到钱:"+money);}
}
(3)动态代理类
在源码当中看到MethodInterceptor就是使用的cglib动态代理
package com.gzl.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 Client {public static void main(String[] args) {final Producer producer = new Producer();/*** 动态代理:* 特点:字节码随用随创建,随用随加载* 作用:不修改源码的基础上对方法增强* 分类:* 基于接口的动态代理* 基于子类的动态代理* 基于子类的动态代理:* 涉及的类:Enhancer* 提供者:第三方cglib库* 如何创建代理对象:* 使用Enhancer类中的create方法* 创建代理对象的要求:* 被代理类不能是最终类* create方法的参数:* Class:字节码* 它是用于指定被代理对象的字节码。** Callback:用于提供增强的代码* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。* 此接口的实现类都是谁用谁写。* 我们一般写的都是该接口的子接口实现类:MethodInterceptor*/Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {/*** 执行producer的任何方法都会经过该方法* @param proxy* @param method* @param args* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的* @param methodProxy :当前执行方法的代理对象* @return* @throws Throwable*/public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//提供增强的代码Object returnValue = null;//1.获取方法执行的参数Float money = (Float)args[0];//2.判断当前方法是不是销售if("saleProduct".equals(method.getName())) {returnValue = method.invoke(producer, money*0.8f);}return returnValue;}});cglibProducer.saleProduct(12000f);}
}
执行结果
4.3.JDK动态代理示例
(1)创建一个接口
package com.gzl.proxy;/*** 对生产厂家要求的接口*/
public interface IProducer {/*** 销售* @param money*/public void saleProduct(float money);/*** 售后* @param money*/public void afterService(float money);
}
(2)实现类
package com.gzl.proxy;/*** 一个生产者*/
public class Producer implements IProducer{/*** 销售* @param money*/public void saleProduct(float money){System.out.println("销售产品,并拿到钱:"+money);}/*** 售后* @param money*/public void afterService(float money){System.out.println("提供售后服务,并拿到钱:"+money);}
}
(3)代理类
package com.gzl.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** 模拟一个消费者*/
public class Client {public static void main(String[] args) {final Producer producer = new Producer();/*** 动态代理:* 特点:字节码随用随创建,随用随加载* 作用:不修改源码的基础上对方法增强* 分类:* 基于接口的动态代理* 基于子类的动态代理* 基于接口的动态代理:* 涉及的类:Proxy* 提供者:JDK官方* 如何创建代理对象:* 使用Proxy类中的newProxyInstance方法* 创建代理对象的要求:* 被代理类最少实现一个接口,如果没有则不能使用* newProxyInstance方法的参数:* ClassLoader:类加载器* 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。* Class[]:字节码数组* 它是用于让代理对象和被代理对象有相同方法。固定写法。* InvocationHandler:用于提供增强的代码* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。* 此接口的实现类都是谁用谁写。*/IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),producer.getClass().getInterfaces(),new InvocationHandler() {/*** 作用:执行被代理对象的任何接口方法都会经过该方法* 方法参数的含义* @param proxy 代理对象的引用* @param method 当前执行的方法* @param args 当前执行方法所需的参数* @return 和被代理对象方法有相同的返回值* @throws Throwable*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//提供增强的代码Object returnValue = null;//1.获取方法执行的参数Float money = (Float)args[0];//2.判断当前方法是不是销售if("saleProduct".equals(method.getName())) {returnValue = method.invoke(producer, money*0.8f);}return returnValue;}});proxyProducer.saleProduct(12000f);}
}
执行结果
Java动态代理和静态代理详解相关推荐
- Java动态数组和静态数组详解
前言必读 读者手册(必读)_云边的快乐猫的博客-CSDN博客 数组: 概念:数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致,不能出现混合类型 1.什么数据类型就只能储存什么数据元素,比 ...
- Gzip的动态压缩和静态压缩详解
Gzip的压缩分为动态压缩和静态压缩.动态压缩简而言之就是nginx上的资源是以原始文件形式存在的,当返回给浏览器的时候再进行压缩,浏览器收到压缩文件再解压缩.静态压缩就是nginx上的资源不是原始文 ...
- java动态代理(JDK和cglib)详解
JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委 ...
- Java设计模式学习06——静态代理与动态代理(转)
原地址:http://blog.csdn.net/xu__cg/article/details/52970885 一.代理模式 为某个对象提供一个代理,从而控制这个代理的访问.代理类和委托类具有共同的 ...
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- Java设计模式之代理(动态代理,静态代理)
代理设计模式,是一种常用的设计模式.顾名思义,在面向对象的世界中,就是一个对象对另一个对象的代理. 如现实生活中,处处都是代理,中介是对业主的代理,王婆是对金莲的代理,经纪人至于艺人的代理,销售代理等 ...
- Java中三种代理方式—— 静态代理与两种动态代理的实现机制
个人博客请访问 http://www.x0100.top 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现 ...
- 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库
文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...
- 动态代理模式(实例化详解)
简介 代理模式通常用于达到对原有系统功能进行扩充的目的, 比如:你刚接手一个别人没有完成的项目,这时你不想动别人原来的代码,还需要添加新功能.这时代理模式,这时代理模式会很好的帮我们解决问题 代理模式 ...
- java 动态代理与静态代理
代理在生活中比较常见,比如常见的代理商,对工厂进行代理,工厂专注生产,代理商负责经销,用户购买商品从代理商手中购买.java中代理分为动态代理与静态代理,其中动态代理比较常见的有jdk动态代理和cgl ...
最新文章
- poj 1698 Alice's Chance 最大流
- pandas apply()函数参数 args
- python中字符串添加r的作用,原始字符串不发生转义
- 算成绩啦(洛谷P5740、P5741题题解,Java语言描述)
- tomcat7 1000并发量配置 tomcat7配置优化
- java栈的内存_JVM的栈内存
- 6425C-Lab5 管理计算机帐户
- Python初识以及变量
- hasOwnProperty()方法与in操作符
- 【大数据】Hadoop快速入门
- 使用Jflash给华大半导体的MCU烧录程序
- matlab电磁铁磁场,Matlab在电磁铁设计计算中的应用
- 杀毒软件工作原理及现有主要杀毒技术
- www.gvlib video.php,求大佬帮忙
- Additions HNUST 1713(矩阵快速幂模板 )
- illegal offset type php,PHP warning Illegal offset type错误解决办法
- win7计算机图标排列,win7文件夹内图标取消自动排列,取消自动排列
- 使用Springboot+MAVEN完成SSM项目的搭建(idea)--小白面试机试题
- 【数学】-1的根号二次方等于多少?
- 新年到,小飞猫来啦~
热门文章
- jQuery图像查看插件Zoom.js
- MATLAB实现QPSK
- python ks值计算_风控模型中的K-S理解以及python实现
- 日志易智能日志分析助力农信银双十一运维降本增效
- 飞Young任意路由器连接[破解路由器限制]
- 页面置换算法之最佳置换算法的模拟(C++)
- android slidemenu 折叠效果,左侧菜单栏折叠展开效果-超级简单
- 饥荒海难创建显示专用服务器,饥荒联机版服务器移除实体指令分享
- 备考cfa一、二级的方法和资料
- android 6.0 oem分区,android系统的分区结构