聊聊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动态代理。

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

 

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