当前位置: 首页 > news >正文

深入理解 Java 代理模式:从基础到实战​

       在软件开发的世界里,设计模式是程序员智慧的结晶,它们为解决常见问题提供了经过验证的最佳实践。代理模式作为一种常用的结构型设计模式,在 Java 开发中有着广泛的应用。本文将深入探讨 Java 代理模式,从基本概念、分类、实现原理到实际应用,带您全面理解这一重要的设计模式。​

一、代理模式基本概念​

代理模式(Proxy Pattern)的核心思想是通过引入一个代理对象来控制对真实对象的访问。代理对象在客户端和真实对象之间起到中介作用,它可以在不修改真实对象代码的情况下,为真实对象添加额外的功能或控制访问行为。​

举个生活中的例子,比如你想租房子,可能会通过房产中介来寻找合适的房源。这里的房产中介就是一个代理,它代表你与房东进行沟通,处理租房过程中的各种事务。你不需要直接与所有房东打交道,只需通过中介即可,中介可以在这个过程中提供​

筛选房源安排看房等额外服务。在软件开发中,代理模式的作用类似,它可以帮助我们更好地管理对真实对象的访问,实现功能的扩展和优化。​

代理模式涉及三个主要角色:​

  1. 抽象主题(Subject):定义了真实主题和代理主题的共同接口,客户端通过该接口访问真实主题和代理主题。​
  2. 真实主题(Real Subject):实际处理业务逻辑的对象,是代理主题所代表的真实对象。、​
  3. 代理主题(Proxy):实现了抽象主题接口,持有对真实主题的引用,负责控制对真实主题的访问,并可以在访问前后添加额外的操作。​

二、代理模式的分类​

在 Java 中,代理模式主要分为静态代理和动态代理两种类型,其中动态代理又包括 JDK 动态代理和 CGLIB 动态代理。​

(一)静态代理​

静态代理是指在编译时就已经确定了代理类的代码,代理类和真实类在编译时是一一对应的。下面通过一个简单的例子来演示静态代理的实现。​

假设我们有一个接口Subject,定义了一个请求方法request():​

public interface Subject {​

void request();​

}​

真实主题类RealSubject实现了该接口,实现具体的请求逻辑:​

public class RealSubject implements Subject {​

@Override​

public void request() {​

System.out.println("RealSubject handling request");​

}​

}​

代理类ProxySubject也实现了Subject接口,并且持有一个RealSubject对象的引用。在代理类的request()方法中,首先可以执行一些前置处理,然后调用真实主题的request()方法,最后执行一些后置处理:​

public class ProxySubject implements Subject {​

private RealSubject realSubject;​

public ProxySubject(RealSubject realSubject) {​

this.realSubject = realSubject;​

}​

@Override​

public void request() {​

System.out.println("Proxy before request");​

realSubject.request();​

System.out.println("Proxy after request");​

}​

}​

客户端使用代理类来访问真实主题:​

public class Client {​

public static void main(String[] args) {​

RealSubject realSubject = new RealSubject();​

ProxySubject proxySubject = new ProxySubject(realSubject);​

proxySubject.request();​

}​

}​

运行结果为:​

Proxy before request​

RealSubject handling request​

Proxy after request​

静态代理的优点是实现简单,容易理解,在编译时就可以确定代理关系。但它的缺点也很明显,当需要代理多个不同的真实主题时,需要为每个真实主题创建对应的代理类,这会导致代码量增加,灵活性较差。​

(二)动态代理​

动态代理是在运行时动态生成代理类的一种方式,它不需要在编译时就确定代理类的代码,而是通过反射机制在运行时生成代理类。动态代理可以分为 JDK 动态代理和 CGLIB 动态代理。​

1. JDK 动态代理​

JDK 动态代理是 Java 自带的动态代理实现,它基于接口实现代理。JDK 动态代理需要使用java.lang.reflect.Proxy类和InvocationHandler接口。​

InvocationHandler接口定义了一个invoke方法,当调用代理对象的方法时,会调用该invoke方法。我们需要实现InvocationHandler接口,在invoke方法中处理对真实主题的调用和额外的逻辑。​

下面以之前的Subject接口和RealSubject类为例,演示 JDK 动态代理的实现。​

首先,创建一个InvocationHandler实现类SubjectInvocationHandler:​

import java.lang.reflect.InvocationHandler;​

import java.lang.reflect.Method;​

public class SubjectInvocationHandler implements InvocationHandler {​

private Object target;​

public SubjectInvocationHandler(Object target) {​

this.target = target;​

}​

@Override​

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {​

System.out.println("JDK Proxy before method");​

Object result = method.invoke(target, args);​

System.out.println("JDK Proxy after method");​

return result;​

}​

}​

然后,在客户端中使用Proxy.newProxyInstance方法生成代理对象:​

import java.lang.reflect.Proxy;​

public class Client {​

public static void main(String[] args) {​

RealSubject realSubject = new RealSubject();​

SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);​

Subject proxy = (Subject) Proxy.newProxyInstance(​

realSubject.getClass().getClassLoader(),​

realSubject.getClass().getInterfaces(),​

handler​

);​

proxy.request();​

}​

}​

运行结果与静态代理类似:​

JDK Proxy before method​

RealSubject handling request​

JDK Proxy after method​

JDK 动态代理的优点是无需引入第三方库,基于接口实现,符合面向接口编程的思想。但它的缺点是只能代理实现了接口的类,对于没有接口的类无法使用。​

2. CGLIB 动态代理​

CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它可以在运行时动态生成子类来实现代理。CGLIB 动态代理不需要依赖接口,它通过继承真实类来生成代理类,因此可以代理没有接口的类。​

使用 CGLIB 动态代理需要引入 CGLIB 的依赖,在 Maven 项目中可以添加以下依赖:​

<dependency>​

<groupId>cglib</groupId>​

<artifactId>cglib</artifactId>​

<version>3.3.0</version>​

</dependency>​

假设我们有一个没有实现接口的真实类RealSubjectNoInterface:​

public class RealSubjectNoInterface {​

public void request() {​

System.out.println("RealSubjectNoInterface handling request");​

}​

}​

创建一个 CGLIB 的代理类,需要实现MethodInterceptor接口,该接口的intercept方法会在调用代理对象的方法时被触发:​

import net.sf.cglib.proxy.MethodInterceptor;​

import net.sf.cglib.proxy.MethodProxy;​

import java.lang.reflect.Method;​

public class CglibProxy implements MethodInterceptor {​

private Object target;​

public CglibProxy(Object target) {​

this.target = target;​

}​

public Object getProxy() {​

net.sf.cglib.proxy.Enhancer enhancer = new net.sf.cglib.proxy.Enhancer();​

enhancer.setSuperclass(target.getClass());​

enhancer.setCallback(this);​

return enhancer.create();​

}​

@Override​

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {​

System.out.println("CGLIB Proxy before method");​

Object result = method.invoke(target, args);​

System.out.println("CGLIB Proxy after method");​

return result;​

}​

}​

客户端使用 CGLIB 代理:​

public class Client {​

public static void main(String[] args) {​

RealSubjectNoInterface realSubject = new RealSubjectNoInterface();​

CglibProxy cglibProxy = new CglibProxy(realSubject);​

RealSubjectNoInterface proxy = (RealSubjectNoInterface) cglibProxy.getProxy();​

proxy.request();​

}​

}​

运行结果为:​

CGLIB Proxy before method​

RealSubjectNoInterface handling request​

CGLIB Proxy after method​

CGLIB 动态代理的优点是无需接口,代理类是真实类的子类,可以代理任何类(除了 final 类)。但由于是通过继承实现的,所以不能代理 final 类,并且性能上可能会有一定的开销。​

(三)JDK 动态代理与 CGLIB 动态代理的对比​

特性​

JDK 动态代理​

CGLIB 动态代理​

依赖​

无需第三方库,Java 自带​

需要引入 CGLIB 库​

代理方式​

基于接口​

基于子类(继承)​

适用场景​

代理实现了接口的类​

代理没有接口的类,或者希望更灵活的代理方式​

性能​

反射调用,性能稍低​

生成子类,性能相对较高​

三、代理模式的应用场景​

代理模式在 Java 开发中有很多实际应用场景,以下是一些常见的例子:​

(一)远程代理​

远程代理用于代理访问远程服务器上的对象。例如,在分布式系统中,客户端需要访问远程服务器上的服务,远程代理可以隐藏网络通信的细节,让客户端像调用本地对象一样调用远程对象的方法。RMI(Remote Method Invocation)就是一种典型的远程代理应用。​

(二)虚拟代理​

虚拟代理用于延迟加载真实对象,只有在需要使用真实对象时才创建它。例如,当处理大对象或需要复杂初始化的对象时,虚拟代理可以在对象被使用前先返回一个简单的代理对象,当真正需要使用时再加载真实对象,从而提高系统的性能和响应速度。​

(三)保护代理​

保护代理用于控制对真实对象的访问权限,确保只有符合条件的客户端才能访问真实对象。例如,在系统中,不同的用户角色可能具有不同的访问权限,保护代理可以在代理对象中检查客户端的权限,决定是否允许访问真实对象。​

(四)日志记录与性能监控​

代理模式可以用于在方法调用前后添加日志记录和性能监控代码,而无需修改真实对象的代码。通过代理对象,我们可以统一在代理类中实现日志记录和性能统计功能,实现代码的复用和解耦。​

四、代理模式的优缺点​

(一)优点​

  1. 解耦:代理模式将客户端与真实对象解耦,客户端通过代理对象访问真实对象,不需要知道真实对象的具体实现细节。​
  2. 扩展性:可以在不修改真实对象代码的情况下,通过代理对象添加额外的功能,符合开闭原则。​
  3. 控制访问:代理对象可以控制对真实对象的访问,实现访问权限控制、延迟加载等功能。​
  4. 集中处理公共逻辑:像日志记录、事务处理、性能监控等公共逻辑可以集中在代理类中实现,避免在真实对象中重复编写代码。​

(二)缺点​

  1. 增加复杂度:引入代理模式会增加系统的类数量和复杂度,特别是在使用动态代理时,需要理解反射机制和相关的 API。​
  2. 性能开销:动态代理通过反射机制调用方法,会带来一定的性能开销;静态代理则需要为每个真实对象创建代理类,增加了代码量。​
  3. 学习成本:对于初学者来说,理解代理模式的原理和实现需要一定的学习成本,特别是动态代理涉及到反射等高级特性。​

五、总结​

代理模式是一种非常实用的设计模式,它通过引入代理对象来控制对真实对象的访问,实现了功能的扩展和优化。在 Java 中,静态代理实现简单,但灵活性较差;动态代理包括 JDK 动态代理和 CGLIB 动态代理,分别适用于不同的场景。JDK 动态代理基于接口,CGLIB 动态代理基于子类,开发者可以根据具体需求选择合适的代理方式。​

代理模式在实际开发中有广泛的应用,如远程代理、虚拟代理、保护代理等,它能够帮助我们更好地管理对象的访问,实现代码的解耦和复用。虽然代理模式存在一定的缺点,但在合适的场景下使用,可以显著提高系统的可维护性和扩展性。​

作为 Java 开发者,掌握代理模式的原理和应用是非常重要的。通过不断地实践和学习,我们可以更好地运用代理模式来解决实际开发中的问题,写出更加优雅、高效的代码。​

相关文章:

  • MiM: Mask in Mask Self-SupervisedPre-Training for 3D Medical Image Analysis
  • Docker宿主机IP获取
  • 智慧工会服务平台建设方案Word(23页)
  • 机器学习-无量纲化与特征降维(一)
  • 爬虫学习————开始
  • AI服务器通常会运用在哪些场景当中?
  • vue dev-tools插件
  • 电动汽车充换电设施可调能力聚合评估与预测 - 使用说明文档
  • 亚马逊跨境新蓝海:解码爱尔兰电商市场的凯尔特密码
  • HDLC(High-Level Data Link Control,高级数据链路控制协议)
  • uniapp-商城-47-后台 分类数据的生成(通过数据)
  • uniapp 不同路由之间的区别
  • 高频数据结构面试题总结
  • 数据类型:List
  • [特殊字符]适合母亲节的SVG模版[特殊字符]
  • 解决 Redis 缓存与数据库一致性问题的技术指南
  • 配置Java Selenium Web自动化测试环境
  • 在Mathematica中加速绘制图形(LibraryLink)
  • Linux——进程信号
  • 【Qt】编译 Qt 5.15.x For Windows 基础教程 Visual Studio 2019 MSVC142 x64
  • 讲座预告|全球贸易不确定情况下企业创新生态构建
  • 股价两天涨超30%,中航成飞:不存在应披露而未披露的重大事项
  • 视频丨习近平同普京会谈:共同弘扬正确二战史观,维护联合国权威和地位
  • 司法部:民营经济促进法明确禁止违规异地执法、利用行政或者刑事手段违法干预经济纠纷
  • AI智能体,是不是可以慢一点? | ToB产业观察
  • 习近平抵达莫斯科伏努科沃专机机场发表书面讲话(全文)