Java代理模式实现与详解(二)

简介: Java代理模式实现与详解(二)

1 为什么要用动态代理

上一次我们详细分析了静态代理模式的原理,并且用代码简单实现了一个静态代理的案例。但是我们会发现在静态代理中代理类与被代理类都需要实现同一个接口,这就说明我们的一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。由此我们JDK为我们提供了动态代理这个概念,即利用一个代理类就可以实现所有被代理的操作。下面我们就来详细分析动态代理到底是怎么一回事。

2 动态代理

2.1什么是动态代理

之前我们了解到静态代理指代理类在程序运行前就已经存在。那么反之动态代理的代理类在的程序运行前是不存在的,也就是说代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的Java代码中的“指示”动态生成的。这么说比较抽象,我们先通过一个案例来看动态代理到底怎么实现的。

2.2.动态代理简单实现

  还是我们之前那个实例:一个班上的同学要向老师提交作业,但是老师并不直接收每个人的作业,都是通过把作业交给学习委员,再由学习委员将作业转交给老师。我们今天用动态代理的方式来将它实现。我们沿用之前写好的Person接口与Student类,具体代码实现请参考上一篇博文《Java代理模式实现与详解(一)》

我们先来写一个代理类MyProxy,通过这个类实现动态代理。代码如下:

 

大家可以看到,这个类实现了一个接口 InvocationHandler,还引入了泛型,并且重写了invoke()方法,在invoke()方法里我们打印了一句话记录我们具体代理执行哪个方法。那为什么要这么做呢?我们下面会对其进行详细的介绍。

代理类我们已经写好了,那么我们来看看怎么实现动态代理呢。编写一个测试类Test.java 用于测试我们的动态代理。

 

运行结果:

 

从代码中我们可以看到,我们先是创建一个被代理的对象,然后创建一个InvocationHandler对象handler并且将要代理的对象与其绑定。接着我们创建了一个代理对象stuProxy,并将handler作为参数传了去,最终通过stuProxy代理执行了submitHomework方法,完成了代理。

在代理类中我们并没有声明我们要代理的类具体是哪个而是在我们的测试类中,也就是我们需要用的时候才创建我们想要代理的被代理对象,并将其传进代理类中,最终完成了代理,这就是动态代理模式。

在代理类中我们实现了一个InvocationHandler接口,在测试类中我们调用了一个Proxy类,由此我们才完成了动态代理功能,那为什么我们要这么做呢?他们又代表着什么呢?我们接下来详细的分析其原理。

3 动态代理原理分析

3.1.InvocationHandler接口

如果想要实现动态代理模式,那么首先必须要观察一个接口:

java.lang.reflect.InvocationHandler;我们进到这个接口的源码去观察:

 

在这个接口里面我们可以看到它只有一个invoke()方法。那这个invoke()方法有什么作用呢?这个方法里面有三个参数,那这三个参数又分别代表什么?我们来看它里面的注释:

 

这个接口里面对三个参数的作用做了详细的解释。我们总结一下:

Object proxy:表示代理类的对象。

Method method表示正在调用的方法。

Object[] args表示接口方法里面接收的参数如果接口方法不使用参数则为null

我们再来看他的返回值类型是什么:

通过给出的注释我们可以知道此方法返回的值一定是相应基本包装对象类的实例。也就是返回我们的代理对象,所以这个方法就属于代理类中调用真实主题类即被代理类的操作方法。但是我们可以发现这个方法里没有所对应的被代理对象,所以在创建这个类对象的时候设置好真实的操作对象。那我们又怎么去进行设置呢?其实在上面的案例中我们已经做出了示范操作。我们要想找到代理对象则要使用java.lang.reflect.Proxy类进行动态创建。这个类是怎么完成创建的呢?我们根据其源码来详细分析。

3.2. java.lang.reflect.Proxy

在这个类中我们主要关注一个方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException   ; 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。正是此方法实现了我们的动态代理功能。我们先来看一下此方法里的参数:

ClassLoader loader指的取得对象的加载器。

Class<?>[] interfaces代理设计模式的核心是围绕接口进行的,我们必须取出全部的接口。所以这个参数指代理类要实现的接口列表。

InvocationHandler h指派方法调用的调用处理程序即代理的实现类。

我们来看这个方法具体的实现代码:

 

我们先来分析上面的部分代码,代码中先检查了代理的实现类对象是否为空,是的话就抛出NullPointerException,接着对传入的接口进行安全检查。我们继续来看代码:

 

这里我们根据给出的注释我们便能明白其作用,作用是查找或者生成指定的代理类,这段代码便是实现动态代理的核心方法,动态代理的思路其实就是生成一个新类。

我们接着往下看:

 

 

同样的我们先看代码注释,意为:使用指定的调用处理程序调用其构造函数,即一个指定接口的代理类实例。至此我们便能明白我们为什么要用此方法区设置我们上面所说的真实的操作对象。我们返回来看之前我们写的案例代码:

 

我们正是通过我们上面所说的Proxy.newProxyInstance方法,设置真实的操作对象,完成动态代理。方法的第一个参数我们通过类加载器取得我们要代理的对象,第二个参数我们取得了代理类要实现的接口列表也就是Person这个类里面的接口列表,最后第三个参数我们将代理的实现类对象传了进去。通过此方法我们获得了一个Person的代理对象。最终通过代理对象完成了代理操作。

4 总结

本文对Java动态代理做了详细的分析,并实现了一个简单的动态代理案例,实现动态代理最关键的就是一个接口InvocationHandler和一个类Proxy。上面动态代理的例子,其实就是一个AOP的简单实现了。SpringAOP实现也用到了ProxyInvocationHandler这两个东西。但是我们从Proxy类可以发现,本文我们分析的Java动态代理只能对接口实现代理,无法实现对class的动态代理。那是否意味着如果没有接口我们就不能使用动态代理模式了呢?有没有什么代理方式能不依赖接口进行代理呢?

目录
相关文章
|
6月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
65 1
|
2月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
6月前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
3月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
6月前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
58 2
|
4月前
|
存储 设计模式 Java
Java面试题:解释代理模式的概念,并举例说明其应用场景。
Java面试题:解释代理模式的概念,并举例说明其应用场景。
63 0
|
4月前
|
设计模式 监控 Java
深入理解Java中的代理模式及其实现
深入理解Java中的代理模式及其实现
|
4月前
|
设计模式 监控 Java
深入理解Java中的代理模式及其实现
深入理解Java中的代理模式及其实现
|
5月前
|
设计模式 监控 安全
设计模式之代理模式(Java)
设计模式之代理模式(Java)
|
5月前
|
设计模式 安全 Java
Java设计模式:代理模式的静态和动态之分(八)
Java设计模式:代理模式的静态和动态之分(八)