你一定能看懂的JDK动态代理
前言:阅读这篇文章前,一定要知道什么是代理模式,具体可以参考这篇文章《设计模式(一):代理模式》。
在《设计模式(一):代理模式》一文中说了,程序员思思买书有两种选择:一种是选择去书厂(目标对象)买;另一种则是去书店(代理对象)买。第二种方式可以称为静态代理,因为这个代理对象是我们自己编写的。而JDK动态代理则是一种系统自动为我们生成代理对象的方式,下面先介绍一下这种方式如何实现。
一、JDK动态代理
首先还是有一家书厂,并且宣称自己有思思想要的书卖。
public interface Book {
public void buyBook();
}
public class BookFactory implements Book{
@Override
public void buyBook() {
System.out.println("思思买的书来自书厂");
}
}
现在思思将使用JDK动态代理的方式去买到和书厂一模一样的书,那应该怎么做呢?
首先思思要找到一个 “神秘人” ,这个人专做各种倒买倒卖的生意。
public class MysteryMen implements InvocationHandler{
private Object target; //思思需要告诉神秘人她想买书厂的书
public MysteryMen(Object target){
this.target = target;
}
//下面是神秘人做的事情,他能帮思思买到书,暂时不用管是怎么做到的。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("神秘人开始买书");
method.invoke(target, args);
System.out.println("神秘人完成买书");
return null;
}
}
找到神秘人买书后,神秘人告诉思思还必须填写下面这个承诺表,才能将书给她。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
思思可是程序员啊,这自然难不倒她,下面是思思买书的具体步骤。
public class SiSi {
public static void main(String[] args) {
Book book = new BookFactory();
MysteryMen mysteryMen = new MysteryMen(book);
Book dynamicBookStore = (Book)Proxy.newProxyInstance(BookFactory.class.getClassLoader(),
BookFactory.class.getInterfaces(), mysteryMen);
dynamicBookStore.buyBook();
}
}
执行 main 方法,结果如下:
二、代码分析
结合上面的例子,下面对整个动态代理的过程做一个简单的梳理。
综合上一篇文章《设计模式(一):代理模式》可以看到,动态代理与静态代理有一个最大的不同点,就是静态代理需要自己编写代理类,即《设计模式(一):代理模式》中的 BookStore 类,再在 main 方法中执行 bookStore.buyBook()
方法来达到目的。而使用动态代理时,这个代理对象是由系统在运行时为我们动态生成的。那系统到底是怎么为我们生成动态代理对象的呢?
首先看示例中,Proxy 类是JDK为我们提供的,它的 newProxyInstance 方法源码是这样写的:
*@param loader the class loader to define the proxy class
*@param interfaces the list of interfaces for the proxy class
* to implement
*@param h the invocation handler to dispatch method invocations to
*@return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- 第一个参数是一个类加载器,这个类加载器必须是加载目标对象字节码的类加载器。
- 第二个参数是指定把动态生成的代理对象挂载到哪个接口下,此处是让动态代理对象与目标对象实现了同一个接口 Book。
- 第三个参数,也是最为重要的一个参数,需要一个InvocationHandler接口的实现类。
- 这个方法返回的是一个动态代理对象。
如果上面的参数不是很理解。具体到文中的例子,应该写成这样:Proxy.newProxyInstance(BookFactory.class.getClassLoader(),
BookFactory.class.getInterfaces(), mysteryMen)
InvocationHandler接口的源码如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
结合上面的例子来看,我们在 invoke 方法里写了这样一段代码method.invoke(target, args)
,这段代码涉及到 Java 反射的内容,相当于通过反射调用了 BookFactory 中的 buyBook 方法。
那系统是怎么知道要去调用BookFactory方法的呢?
使用Debug调试可以看到:
- method就是 buyBook 方法。
- 而args是方法参数,此处为 null。
- $Proxy0就是系统为我们动态生成的代理对象。通过在 main 方法里加上
System.out.println(dynamicBookStore.toString)
可验证。
method.invoke(target, args)
中还有一处还没有说到,那就是第一个参数target。这个参数为系统指明了调用哪个实现类的 buyBook 方法。在示例中我们通过构造函数的传参的方式,在MysteryMen类中传入了一个 BookFactory 类的引用,也是为了反射调用目标方法做准备的。
至此我们重新梳理一下整个动态代理的过程。
三、动态代理过程解析
结合文章开始的例子,要写一个动态代理,首先得有个接口(Book)和该接口的实现类(BookFactory),这里的实现类其实就是被代理对象,或者称为目标对象 target ;然后写一个 InvocationHandler 实现类,这个实现类的 invoke 方法里包含着我们的处理逻辑,例如对目标对象方法的调用,或者添加上我们自己想要实现的处理逻辑;最后通过 Proxy 类的静态方法 newProxyInstance 来生成动态代理对象。
再次使用Debug调试,将断点打在dynamicBookstore.buyBook()
处,再单步进入此方法可以看到,程序立刻跳入了 MysteryMen 的 invoke 方法:
这张图说明了一点,那就是执行dynamicBookstore.buyBook()
方法其实是执行了 InvocationHandler 接口实现类的 invoke 方法,invoke 方法里才是真正的处理逻辑地方。
在 invoke 方法里我们可以做任何我们想要的处理逻辑,例如在反射调用方法前后做一些操作,甚至不执行对目标方法的反射调用。
三、动态代理的优势
目前来说,我们使用动态代理完成的功能都能换用静态代理来完成。那么如此的话,动态代理到底有什么优势呢?
在静态代理中,一个目标对象对应这一个代理对象,如果目标对象过多,将会加重程序的代码量。而对于动态代理来说,如果我们想要对多个类进行代理,只需通过 newProxyInstance 方法让程序为我们动态生成代理对象即可,减少了代码工作量。并且在目标对象和动态代理对象之间还增加了一层 InvocationHandler 对目标方法进行拦截,一定程度上实现了解耦。
还有最重要的一点,就是静态代理在编译期间就需要指定对哪一个类进行代理,并写好该类的代理类。而动态代理则可以在运行期间确定需要对哪一个类进行代理,增加了系统的灵活性,其中运用了 Java 反射技术。
你一定能看懂的JDK动态代理的更多相关文章
- JDK动态代理为什么必须要基于接口?
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 前几天的时候,交流群里的小伙伴抛出了一个问题,为什么JDK的动态代理一定要基于接口实现呢? 好的安排,其实要想弄懂这个问题还是需要一些关于代理和 ...
- 有点深度的聊聊JDK动态代理
在接触SpringAOP的时候,大家一定会被这神奇的功能所折服,想知道其中的奥秘,底层到底是如何实现的.于是,大家会通过搜索引擎,知道了一个陌生的名词:动态代理,慢慢的又知道了动态代理有多种实现方式, ...
- 从静态代理,jdk动态代理到cglib动态代理-一文搞懂代理模式
从代理模式到动态代理 代理模式是一种理论上非常简单,但是各种地方的实现往往却非常复杂.本文将从代理模式的基本概念出发,探讨代理模式在java领域的应用与实现.读完本文你将get到以下几点: 为什么需要 ...
- 【原创】JDK动态代理,此次之后,永生难忘。
动态代理,这个词在Java的世界里面经常被提起,尤其是对于部分(这里强调“部分”二字,因为有做了一两年就成大神的,实力强的令人发指,这类人无疑是非常懂动态代理这点小伎俩的)做了一两年新人来说,总是摸不 ...
- JDK动态代理,此次之后,永生难忘
出自:http://www.cnblogs.com/dreamroute/p/5273888.html#3839242 首先感谢"神一样的存在"的文章! 动态代理,这个词在Java ...
- JDK 动态代理的实现
JDK 动态代理的实现 虽然在常用的 Java 框架(Spring.MyBaits 等)中,经常见到 JDK 动态代理的使用,也知道了怎么去写一个 JDK 动态代理的 Demo,但是并不清楚实现原理. ...
- 静态代理和jdk动态代理
要说动态代理,必须先聊聊静态代理. 静态代理 假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志. 你如何在不修改已有代码的前提下,完成这个需求? 我首先想到的是静态代理.具体做法是: 1 ...
- JDK动态代理
一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...
- 静态代理和利用反射形成的动态代理(JDK动态代理)
代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...
随机推荐
- CUDA中的常量内存__constant__
GPU包含数百个数学计算单元,具有强大的处理运算能力,可以强大到计算速率高于输入数据的速率,即充分利用带宽,满负荷向GPU传输数据还不够它计算的.CUDA C除全局内存和共享内存外,还支持常量内存,常 ...
- WPF依赖属性(续)(3)依赖属性存储
原文:WPF依赖属性(续)(3)依赖属性存储 在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性 ...
- IOC介绍及其简单实现
预备知识: Java反射原理,XML及其解析 IOC:Inversion of Control,控制反转,它最主要反映的是与传统面向对象(OO)编程的不同.通常我们编程实现某种功能都需要几个对象相 ...
- 矿Spring入门Demo
第一步:输入Spring jar 包裹 Spring核心包(4个) 日志包(2个) jdbc模板支持(1个) spring-jdbc-3.2.0.RELEASE.jar 模板相关事务处理包(1 ...
- 理解c#
首先在介绍c#的时候我们要先理解什么是.NET,.NET就是微软的用来实验XML,Web Services,SOA(面向服务的体系结构service-oriented architecture)和敏捷 ...
- 给 Web 开发人员推荐的通用独立 UI 组件(一)(按钮很不错)
现代 Web 开发在将体验和功能做到极致的同时,对于美观的追求也越来越高.在推荐完图形库之后,再来推荐一些精品的独立 UI 组件.这些组件可组合在一起,形成美观而交互强大的 Web UI . 给 We ...
- Java transient关键字【转】
转自:http://www.blogjava.net/fhtdy2004/archive/2009/06/20/286112.htmlVolatile修饰的成员变量在每次被线程访问时,都强迫从主内存中 ...
- 1.在windows下安装rabbitMQ
a .RabbitMQ是用erLang语言写的,所以我们在安装rabbitMQ之前要先安装erLang. 要安装最新版本的请分别前往 www.erlang.org和www.rabbitmq.com网站 ...
- 在服务器中无法自宿主启动一个webapi项目,引出查看系统端口是否被占用
在windows server下启动一个webapi的.net core项目报错 如下图: 让我以为服务器还需要别的配置.后来发现原来是线程被占用了. 在windows下查看某线程是否被占用的命令如下 ...
- C#更改控制台文本的前景色和背景色
关键字:C# NET 控制台 前景色 背景色地址:http://www.cnblogs.com/txw1958/archive/2012/12/07/csharp-console-color.html ...