引言
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。通过阅读本文,读者将会对 Java 动态代理机制有更加深入的理解。本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。
代理:设计模式
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
在现实生活中,这种情形非常的常见,比如请一个律师代理来打官司。
从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。
代理类实现:静态代理
代理模式的实现方式有两种,分别是静态代理和动态代理,我们先看静态代理的实现;
针对上图中的UML图,我们看下Subject 接口的code:
public interface Subject {
void visit();
}
RealSubject 的实现:
1public class RealSubject implements Subject{
2 @Override
3 public void visit() {
4 System.out.println("real visit....");
5 }
6}
ProxySubject 代理类的实现:
1public class ProxySubject implements Subject{
2
3 private Subject subject;
4
5 public ProxySubject(Subject subject){
6 this.subject = subject;
7 }
8
9 @Override
10 public void visit() {
11 System.out.println("before......");
12 subject.visit();
13 System.out.println("end.........");
14 }
15}
Client的实现:
1public class Client {
2 public static void main(String[] args) {
3 Subject realSubject = new RealSubject();
4 ProxySubject proxySubject = new ProxySubject(realSubject);
5 proxySubject.visit();
6 }
7}
执行结果如下:
1before......
2real visit....
3end.........
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。静态代理的缺点也很明显,如果需要代理若干Subject,那么就需要implement若干Subject接口方法,而Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
代理实现:JAVA动态代理
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。
其步骤如下:
编写一个委托类的接口,即静态代理的(Subject接口)
实现一个真正的委托类,即静态代理的(RealSubject类)
创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法
在测试类中,生成动态代理的对象。
第一二步骤,和静态代理一样。我们主要看第三步,代码如下:
1public class JavaProxy implements InvocationHandler {
2
3 private Object object;
4
5 public JavaProxy(Object object) {
6 this.object = object;
7 }
8
9 @Override
10 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
11 System.out.println("before >>>>>>>>");
12 Object result = method.invoke(object, args);
13 System.out.println("end >>>>>>>>>>>");
14 return result;
15 }
16}
第四步,创建动态代理的对象
1 Subject realSubject = new RealSubject();
2 JavaProxy proxy = new JavaProxy(realSubject);
3 ClassLoader classLoader = realSubject.getClass().getClassLoader();
4 Subject subject = (Subject) Proxy.newProxyInstance(classLoader,new Class[]{Subject.class},proxy);
5 subject.visit();
运行结果如下:
1before >>>>>>>>
2real visit....
3end >>>>>>>>>>>
创建动态代理的对象,需要借助Proxy.newProxyInstance。该方法的三个参数分别是:
ClassLoader loader表示当前使用到的appClassloader。
Class[] interfaces表示目标对象实现的一组接口。
InvocationHandler h表示当前的InvocationHandler实现实例对象。
java 自带的动态代理为什么一定要实现接口
java自带的动态代理为什么一定要实现接口,其实这个问题也一直困扰着我,我们从创建代理函数看起,即
1public static Object newProxyInstance(ClassLoader loader,
2 Class
[] interfaces, InvocationHandler h)
3 throws IllegalArgumentException
通过源码可以看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),
1Class cl = getProxyClass(loader, interfaces);
然后通过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。
1 final Constructor
cons = cl.getConstructor(constructorParams);
2 final InvocationHandler ih = h;
3if (!Modifier.isPublic(cl.getModifiers())) {
4 AccessController.doPrivileged(new PrivilegedAction
() {
5 public Void run() {
6 cons.setAccessible(true);
7 return null;
8 }
9 });
10 }
11 return cons.newInstance(new Object[]{ h });
接口起什么作用呢,于是又看getProxyClass方法的代码,这个源码很长,就不细说了。大致分为三段:
第一:验证
第二:缓存创建新类的结构,如果创建过,则直接返回。(注意:这里的KEY就是接口列表)
第三:如果没有创建过,则创建新类
创建代码的过程代码如下:
1 /*
2 * //获得代理类数字标识
3 */
4 long num = nextUniqueNumber.getAndIncrement();
5 String proxyName = proxyPkg + proxyClassNamePrefix + num;
6
7 /*
8 * 调用class处理文件生成类的字节码,根据接口列表创建一个新类,这个类为代理类,
9
10 */
11 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
12 proxyName, interfaces, accessFlags);
13 try {
14//通过JNI接口,将Class字节码文件定义一个新类
15 return defineClass0(loader, proxyName,
16 proxyClassFile, 0, proxyClassFile.length);
17 } catch (ClassFormatError e) {
18 /*
19 * A ClassFormatError here means that (barring bugs in the
20 * proxy class generation code) there was some other
21 * invalid aspect of the arguments supplied to the proxy
22 * class creation (such as virtual machine limitations
23 * exceeded).
24 */
25 throw new IllegalArgumentException(e.toString());
26 }
27 }
可以猜测到接口创建的新类proxyClassFile 不管采用什么接口,都是以下结构
1public class $Proxy1 extends Proxy implements 传入的接口{ }
生成新类的看不到源代码,不过猜测它的执行原理很有可能是如果类是Proxy的子类,则调用InvocationHandler进行方法的Invoke. 因为新生成的类已经extends 了Proxy,而且JAVA不能多继承,因此接口是最好的实现方式了。
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
动态代理实现:Cglib
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
1、首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)
1public class MyBook {
2
3 void addBook(){
4 System.out.println("add book...");
5 }
6}
2、实现 MethodInterceptor方法代理接口,创建代理类
1public class MyCglibProxy implements MethodInterceptor {
2
3 @Override
4 public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
5
6 System.out.println("before——————");
7 Object result = methodProxy.invokeSuper(o, args); //调用业务类(父类中)的方法
8 System.out.println("end——————");
9
10 return result;
11 }
12}
3、创建业务类和代理类对象
1 MyBook myBook = new MyBook();
2
3 Enhancer enhancer = new Enhancer();
4 enhancer.setSuperclass(myBook.getClass());
5 //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
6 MyCglibProxy myCglibProxy = new MyCglibProxy();
7 enhancer.setCallback(myCglibProxy);
8 // 创建动态代理类对象并返回
9 MyBook book = (MyBook) enhancer.create();
10
11 book.addBook();
执行结果如下:
1before——————
2add book...
3end——————
总结
1、静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
2、JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
3、CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
相关下载 |
《奇葩战斗家》是一款热门的手游,它的新上线的模式也非常有趣。很多小伙伴们还不知道奇葩战斗家的新模式是什么,接下来小编就给大家介绍一下新模式都有哪详情>>
原标题:产教融合 改革有了新路径(政策解读) 近日,国家发展改革委、教育部等6部门印发《国家产教融合建设试点实施方案》(以下简称《实施方案》)提出,通过详情>>
原标题:产教融合 改革有了新路径(政策解读) 城市为节点、行业为支点、企业为重点 产教融合 改革有了新路径(政策解读) 近日,国家发展改革委、教育部等6详情>>
FPX电子竞技俱乐部 :斩破过去的权威,面前的虚妄,在火焰中燃烧起新的凤凰 RNG电子竞技俱乐部 :走过辉煌和低谷,回归内心,带着过去之人的荣耀,走向新生 iG电子竞技俱详情>>
原标题:谁来解读基因数据?“Uber模式”人工咨询能否行得通 在基因检测甚火的今天,无论是美国的 23andMe,还是华大基因,大家应该都有所耳闻。但是,你是详情>>
2019英雄联盟全球总决赛主题曲在今天凌晨入围赛阶段比赛结束后终于发布了,今年的主题曲名为《Phoenix》,中文译名涅槃。较往年更为特别的是,今年的主题曲MV是首次由参赛选详情>>
阴阳师在10月9日于百鬼弈上线了一位新的SR式神“纸舞”,她有着较为复杂的技能机制,让许多小伙伴大呼看不懂。接下来就让我们一起来看一看纸详情>>
从《Legend Never Die》歌颂坚持与信念,再到《Rise》里讲述的Ambition执着登山的故事,虽然我们等待了太久太久,但是S9的主题曲还是走到了我们面详情>>
原标题:【中国那些事儿】“十一”黄金周消费亮点多 外媒这样解读 中国日报网10月8日电 近年来,伴随中国经济进入转型升级新阶段,国际上唱衰中国经济详情>>
原标题:恢复出厂设置,对手机本身有什么影响?中国电信解读 IT之家10月8日消息 今日,中国电信官方发文表示,当手机经常出现卡顿、内存不足等问题时,恢复出详情>>
原标题:试点“团购”扩围到全国 让更多患者用上质优价廉药品(政策解读) 近日,国家组织药品集中采购和使用试点全国扩围拟中选结果公布。拟中选结果显详情>>
原标题:正惠享分销系统十大魅力优势解读! 试问,试想: 1.你平常都在哪里买家庭用品? 2.你们一家人大概一个月要消费多少钱? 3.在那个超市消费了那么多钱详情>>
原标题:阴阳师:小袖之手技能全面解读,这个式神你会用吗? 小袖之手这个式神已出道很久了,但是目前依然有很多玩家不清楚她的技能,也有可能是她本身就业岗位详情>>
《逆水寒》金秋狂欢资料片“剑和远方”正式上线,下面为大家带来十一大宋出行手册,国庆活动详细解读。 马上就要放长假了,逆小寒已经定好飞回三清山的机票,就等着放假详情>>
《逆水寒》金秋狂欢资料片“剑和远方”正式上线,下面为大家带来十一大宋出行手册,国庆活动详细解读。 马上就要放长假了,逆小寒已经定好飞回三清山的机票,就等着放假详情>>
《逆水寒》金秋狂欢资料片“剑和远方”正式上线,下面为大家带来十一大宋出行手册,国庆活动详细解读。 马上就要放长假了,逆小寒已经定好飞回三清山的机票,就等着放假详情>>
《逆水寒》金秋狂欢资料片“剑和远方”正式上线,下面为大家带来十一大宋出行手册,国庆活动详细解读。 马上就要放长假了,逆小寒已经定好飞回三清山的机票,就等着放假详情>>
《逆水寒》金秋狂欢资料片“剑和远方”正式上线,下面为大家带来十一大宋出行手册,国庆活动详细解读。 马上就要放长假了,逆小寒已经定好飞回三清山的机票,就等着放假详情>>
原标题:全链路风控解决方案深度解读 一、互联网化带来的业务风控难题 互联网社区内出现大量广告水文,电商营销活动中面临薅羊毛、刷单等问题,航旅出详情>>
1960年和1975年,中国登山队两次登顶珠峰,两次都上了人民日报头版。电影《攀登者》就讲述了这个“完成不可能完成的任务”的故事。 在这个大片云集的国庆档,观众对《攀登者》的详情>>
原标题:阴阳师:R式神进阶指南,式神实力大解读,萌新的好帮手 平安京内逍遥游,精彩内容看不够。大家好,我是你们的老朋友游界逍遥游。 这篇文章是写给详情>>
原标题:法仔课堂第69期——企业专利风险规避与政策解读 2019年8月25日亚马逊被美国顶级出版商协会(AAP)的七家出版巨头起诉。原因是Audible推出了一详情>>
原标题:50家高职院校10月聚沈解读扩招政策 原标题:50家高职院校10月聚沈解读扩招政策 9月25日,记者从沈阳市政府召开的新闻发布会上获悉,10月12日,201详情>>
王者荣耀今天的每日一题的问题是《大神问答》第5期中,和呆阿拿一起解读西施控制技能的职业选手是谁?是不是还有很多小伙伴对王者荣耀9月25日的每日一题答详情>>
摩尔科技很多游戏电竞玩家对显示器的要求很高,其中有一项参数是显示器响应事件,那么响应时间到底是什么呢?在使用过程中,响应时间是用来描述液晶像素的速度,以及它们从一种颜色到详情>>
和平精英的各位特种兵们,雨落想问一下,周五晚上小白裙的直播首秀大家有没有围观呢?之前立下众多flag的小白裙,联手人气主播不求人,说要为自己真正的游戏实力正名!在游戏当中小白裙详情>>
原标题:国家粮食和物资储备局相关负责人解读今年秋粮收购情况 人民网北京9月23日电 (冯粒)19日,国家粮食和物资储备局等八部门联合下发《关于切实做详情>>
原标题:解读5G时代(1):解读的意义 未来是善变的,它喜欢新的故事。 2019年被称之为5G时代的元年。而每一个时代的更迭,都伴随着新故事的发生。接下来详情>>
原标题:GDP大省成互联网“荒漠”?解读山东互联网发展现状 图片来源@全景网 文丨齐鲁人才网 互联网作为近年来发展迅猛的新兴行业,现已成为推动地详情>>
原标题:取个好名解读:公司旗下多款产品,综合品牌命名策略 最近服务的客户中,不少遇到这样的问题:公司旗下多款产品,究竟要以什么策略来命名?是应该统一到详情>>
原标题:辽宁省2019年第二阶段高职扩招政策解读 按照《辽宁省高职扩招专项工作实施方案》和《辽宁省教育厅关于做好2019 年辽宁省高职扩招专项详情>>
原标题:中重度游戏海外市场趋势分析及盈利模式探索机会解读 8月29日,由罗斯基联合腾讯云、闯奇数据联合主办的“游戏出海那些事”主题活动在广州举详情>>
原标题:国务院新闻办公室召开发布会对《关于促进全民健身和体育消费推动体育产业高质量发展的意见》进行解读 央广网北京9月19日消息(记者张闻)据中详情>>
原标题:【解读篇】从倒数到第一的艰难跨越 编者按:衡水市位于河北省东南部,不论面积、人口、经济体量,均在河北省内排名靠后,衡水公安机关全省警力最少详情>>
原标题:华为轮值董事长胡厚崑解读计算战略 对“售卖5G技术”等问题这样回应 5G技术是当下华为在全球市场的一大竞争优势,按照创始人任正非的说法,华详情>>
原标题:LOL官方漫画《拉克丝》解读:助你快速浏览完整剧情 近日,英雄联盟宇宙官网发布了漫画《拉克丝》的第五话,也就是这一整部的终章。作为拳头和漫详情>>