聊聊Java动态代理(上)

简介: 前言在之前的文章《聊聊设计模式之代理模式》中,笔者为大家介绍了代理模式,在这里简单回顾一下。代理模式的作用是提供一个代理来控制对一个对象的访问,因此我们可以很方便地实现对一个对象的延迟加载,或者在调用一个对象的方法时加入一些业务逻辑。

前言

在之前的文章《聊聊设计模式之代理模式》中,笔者为大家介绍了代理模式,在这里简单回顾一下。代理模式的作用是提供一个代理来控制对一个对象的访问,因此我们可以很方便地实现对一个对象的延迟加载,或者在调用一个对象的方法时加入一些业务逻辑。然而之前介绍的代理模式属于静态代理,其缺点是如果目标接口改变了,则目标类跟代理类都会受影响,不太灵活。不过在Java中还有一种代理模式叫动态代理,可以弥补静态代理的缺陷。接下来我们将进行详细介绍。

Java动态代理

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler接口,另一个则是 Proxy类,它们是Java动态代理的基础。

我们先看下InvocationHandler接口的API介绍:每个代理类的实例都关联到了一个InvocationHandler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。也就是说我们必须实现该接口,并在invoke方法中实现代理逻辑。

接下来再来看下Proxy类的API介绍:Proxy类提供了创建动态代理类和实例的静态方法,并且由这些静态方法创建的代理类都是Proxy类的子类。简而言之,Proxy类是用来创建动态代理类和实例的。那具体要怎么使用Java的动态代理呢?接下来我们以之前的文章《聊聊设计模式之代理模式》中的例子为基础,将原来的静态代理改造成动态代理。

使用Java动态代理

首先我们来回顾一下,在文章《聊聊设计模式之代理模式》中,我们讲了一个用户登录的例子,我们的需求是在基础的用户登录功能之上,需要增加对违规用户的过滤,在该文章中,我们使用了静态代理实现该功能,接下来我们将该例子改成使用Java动态代理实现。

首先,先定义登录接口:

 

接着,再实现最基本的登录功能:

可以看到,最基础的登录功能是允许所有用户进行登录。接下来我们实现动态代理逻辑:

其中,代理逻辑是在invoke方法里实现的,其是接口InvocationHandler的抽象方法,在这里我们对其进行了实现。invoke方法有3个参数:proxy、method和args,proxy是代理对象的实例,method是接口方法,args是接口方法参数。在invoke方法的实现中,我们先对userId进行过滤,如果符合登陆条件就调用正常的登录逻辑进行登录。正常的登录逻辑是通过method反射调用实现的,由于method的反射调用需要传入被代理对象(即真实对象),所以我们在构造方法中传入了被代理对象,从而在invoke中能实现对被代理对象的方法的调用(也就是正常的登录逻辑的调用)。

getProxyInstance方法是用来获取代理对象的,其使用了Proxy类的静态方法newProxyInstance,其有3个参数,分别是:classLoader、interfaces和invocationHandler,classLoader是类加载器,interfaces是代理对象实现的接口数组,invocationHandler则是实现代理逻辑的InvocationHandler子类对象。客户端调用getProxyInstance方法则可获得动态代理对象,并使用该动态代理对象进行调用。

接下来,我们写一个客户端测试一下:

结果输出如下:

可以看到我们使用动态代理也能实现对违规用户进行过滤的功能。

动态代理的思考

动态代理之所以称作动态代理,是因为代理类跟代理对象是JVM在运行时动态生成的。我们在之前的文章《聊聊设计模式之代理模式》里提到代理模式的代理对象跟被代理对象需要有相同的父类,通常来讲是相同的接口,而在上述动态代理中我们并没有出现实现了LoginService接口的代理类的代码,大家不要误以为DynamicProxyHandler类就是代理类,其实它只是实现了代理逻辑而已,因为它并没有实现LoginService接口,所以不是代理类。那么我们如何验证代理类和代理对象是在运行时产生的呢?写个客户端测试一下就知道了。

其输出如下:

可以看到代理类的名称是com.sun.proxy.$Proxy0,这是JVM对代理类的同统一命名规范,如果有多个代理类,则后面的代理类名称为$Proxy1、$Proxy2……以此类推。再者,代理类实现的接口是LoginService,这符合代理模式的定义,即代理类跟被代理类需要有相同的父类。此外,我们还可以知道代理类的父类是Proxy类,这一点在上文介绍Proxy类的时候已经提及。

既然动态代理类有一个父类Proxy,由于Java单继承的特点,意味着被代理类跟代理类的共同父类只能是接口,这是Java动态代理的限制。不信的话我们可以把LoginService改成抽象类试试看,代码我就不贴出来了,这里只给大家看下运行结果:

可以看到在获取代理对象的时候报错了,原因是代理类已经继承了Proxy类,没办法再继承额外的抽象类了。

代理模式的另外的缺点就是性能问题,因为代理类跟代理对象是在运行时动态生成的,所以相比静态代理而言会损失部分性能,所以使用时需要权衡性能与其他因素。

前文提高,Java动态代理的代理类跟被代理类必须显示地实现接口,那对于遗留系统而言,可能没办法做到这一点,既然如此,有没有其他办法实现动态代理呢?当然是有的,由于篇幅所限,下次再为大家介绍Java中动态代理的其他实现方式——CGLib动态代理。

如果觉得这篇文章对你有帮助,可以扫描下方二维吗,获取更多精彩内容:

 

相关文章
|
21天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
17 0
[Java]静态代理与动态代理(基于JDK1.8)
|
1月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
19 1
|
25天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
1月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
4月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
41 2
day27:Java零基础 - 动态代理
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
212 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
172 0
|
4月前
|
开发框架 Java Android开发
Java中的类反射与动态代理详解
Java中的类反射与动态代理详解
|
4月前
|
Java 数据安全/隐私保护
Java中的动态代理机制详解
Java中的动态代理机制详解
|
4月前
|
Java
Java中的反射与动态代理机制详解
Java中的反射与动态代理机制详解