【设计模式-结构型】代理模式

news/2025/1/19 13:34:11/

一、什么是代理模式

        在港片中,经常能看到一些酷炫的大哥被警察抓了,警察会试图从他们口中套出一些关键信息。但这些大哥们通常会非常冷静地回应:“我有权保持沉默,我要找我的律师。” 

        这个律师就像是大哥的“法律盾牌”,全权处理所有法律事务。律师的角色不仅仅是代理大哥发言,更是在法律的战场上为大哥披荆斩棘。具体来说,律师会做以下几件事情:

  1. 准备法律文件:律师会精心准备各种法律文件,确保每一份文件都无懈可击,为大哥的辩护打下坚实的基础。

  2. 与检察官沟通:律师会与检察官进行多轮沟通,争取对大哥最有利的条件,甚至可能会在法庭上展开一场激烈的辩论。

  3. 法庭辩护:在法庭上,律师会代表大哥发言,用专业的法律知识和丰富的经验,为大哥进行有力的辩护,确保大哥的权益不受侵犯。

  4. 提供法律建议:律师还会向大哥提供专业的法律建议,帮助他了解自己的权利和义务,让他在法律的框架内做出最明智的决定。

        这个过程就像是一场精心策划的战役,律师作为代理,不仅代理了大哥的发言,还在操作执行前后添加了诸多增强处理,确保大哥在法律的保护下安然无恙。这种场景在港片中屡见不鲜,不仅展示了法律的严肃性,还增添了一丝戏剧性和紧张感,让人看得津津有味。

       港片中的大哥,就是运用代理模式的行家能手。下面来说代理模式代理模式是一种结构型的设计模式,代理对象(律师)具备真实对象(犯事大哥)的功能,并代替真实对象完成相应操作(律师代表大哥讲话),同时可以在操作执行前后进行增强处理(准备法律文件、与检察官沟通等)。

二、为什么用代理模式

        通过一个形象的例子我们知道了代理模式,通过这个例子我们可以聊一下为什么要用代理模式(请律师):

  • 权限控制(保护目标对象):保护客户的隐私和权益,避免不当操作。律师作为代理,可以控制犯罪嫌疑人与外界的交流,确保所有信息的传递都在法律框架内进行。例如,律师可以决定哪些信息可以透露给检察官,哪些信息需要保密。

  • 优化性能(提高效率)代理模式可以实现资源的延迟加载和缓存,提高资源利用效率,减少不必要的资源消耗。律师在需要时才会准备具体的法律文件,而不是一开始就进行大量的准备工作。例如,只有在确定案件进入法庭审理阶段时,律师才会准备详细的辩护词。

  • 增加功能(减轻目标对象的负担):代理模式可以在代理对象中添加额外的功能,而不需要修改原始对象的代码。

  • 简化客户端代码:客户端可以更专注于核心业务,而不必处理复杂的细节。犯罪嫌疑人只需要与律师沟通,而不需要直接处理复杂的法律事务。

三、代理模式示例

代理对象分为2类分别是静态代理和动态代理

3.1 静态代理

        静态代理需要我们自己去实现代理模式。下面以请律师为demo实现一个静态代理的示例。从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理。

1、定义法律援助接口

public interface LegalService {void representClient();
}

2、目标对象(大哥) 

public class Suspect implements LegalService {@Overridepublic void representClient() {System.out.println("我是大哥,我需要律师为我进行辩护.");}
}

3、实现代理(律师)

public class LawyerProxy implements LegalService {private Suspect suspect;public LawyerProxy(Suspect suspect) {this.suspect = suspect;}@Overridepublic void representClient() {prepareLegalDocuments();communicateWithProsecutor();suspect.representClient();provideLegalAdvice();}private void prepareLegalDocuments() {System.out.println("准备法律文件.");}private void communicateWithProsecutor() {System.out.println("与检察官进行战略沟通,协商有利条件.");}private void provideLegalAdvice() {System.out.println("为嫌疑人提供专业法律建议,确保其权利得到保护.");}
}

4、 场景

public class Main {public static void main(String[] args) {Suspect suspect = new Suspect();LegalService lawyer = new LawyerProxy(suspect);lawyer.representClient();}
}//输出场景内容
准备法律文件.
与检察官进行战略沟通,协商有利条件.
我是大哥,我需要律师为我进行辩护.
为嫌疑人提供专业法律建议,确保其权利得到保护.

3.2 动态代理

        动态代理是开源工具提供给我们实现好的工具可以直接使用(动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务。其实运用的是反射机制)。目前来说接触到的动态代理主要有2种分别对应的是jdk实现的动态代理 和 cglib动态代理。

3.2.1 jdk动态代理(接口代理

3.2.1.1 jdk相关类和核心方法简单描述

        jdk动态代理主要使用的是 java.lang.reflect包。

        核心类InvocationHandler接口    Proxy类

        核心方法:invoke newProxyInstance

public Object invoke(Object proxy,Method method,Object[] args) throws Throwablepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
 3.2.1.2 jdk动态代理代码示例  

法律援助接口和目标对象(大哥)不用变使用静态代理的代码,其他进行修改。

1、实现InvocationHandler ,作为代理处理器

InvocationHandler,这个处理器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class LawyerProxyHandler implements InvocationHandler {private final LegalService realLawyer;public LawyerProxyHandler(LegalService realLawyer) {this.realLawyer = realLawyer;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在调用真实方法之前执行的逻辑preHandle(args);// 调用真实方法Object result = method.invoke(realLawyer, args);// 在调用真实方法之后执行的逻辑postHandle(args);return result;}private void preHandle(Object[] args) {System.out.println("律师正在准备法律文件和证据.");}private void postHandle(Object[] args) {System.out.println("律师已完成对委托人的代理.");}
}

2、创建具体代理对象(律师)

import java.lang.reflect.Proxy;public class LegalServiceProxy {public static LegalService getProxyInstance(LegalService realLawyer) {return (LegalService) Proxy.newProxyInstance(realLawyer.getClass().getClassLoader(), // 目标类的类加载器new Class<?>[]{LegalService.class}, // 目标类实现的接口new LawyerProxyHandler(realLawyer) // 代理处理器);}
}

3、使用场景

public class Main {public static void main(String[] args) {// 创建真实对象LegalService dg = new RealLawyer();// 创建代理对象LegalService lawyerProxy = LegalServiceProxy.getProxyInstance(dg);// 通过代理对象调用方法lawyerProxy.representClient();}
}//输出
律师正在准备法律文件和证据.
我是大哥,我需要律师为我进行辩护.
律师已完成对委托人的代理.

3.2.2 cglib动态代理(继承代理

        从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。

1、大哥类要改一下,因为没有接口

public class Suspect{@Overridepublic void representClient() {System.out.println("我是大哥,我需要律师为我进行辩护.");}
}

2、 实现MethodInterceptor,作为代理处理器

实现MethodInterceptor接口,这个拦截器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class LawyerProxyInterceptor implements MethodInterceptor {private final Suspect suspect;public LawyerProxyInterceptor(Suspect suspect) {this.suspect = suspect;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在调用真实方法之前执行的逻辑preHandle(args);// 调用真实方法Object result = proxy.invokeSuper(obj, args);// 在调用真实方法之后执行的逻辑postHandle(args);return result;}private void preHandle(Object[] args) {System.out.println("律师正在准备法律文件和证据.");}private void postHandle(Object[] args) {System.out.println("律师已完成对委托人的代理.");}
}

3、 创建具体代理对象(律师)

        使用CGLIB的Enhancer类创建代理对象。这个代理对象将代理真实对象,并在调用方法时通过MethodInterceptor进行拦截。

import net.sf.cglib.proxy.Enhancer;public class LegalServiceProxy {public static RealLawyer getProxyInstance(Suspect suspect) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(suspect.getClass()); // 设置目标类enhancer.setCallback(new LawyerProxyInterceptor(suspect)); // 设置回调return (Suspect) enhancer.create(); // 创建代理对象}
}

4、使用场景 

public class Main {public static void main(String[] args) {// 创建真实对象Suspect suspect = new Suspect();// 创建代理对象Suspect lawyerProxy = LegalServiceProxy.getProxyInstance(suspect);// 通过代理对象调用方法lawyerProxy.representClient();}
}//输出
律师正在准备法律文件和证据.
我是大哥,我需要律师为我进行辩护.
律师已完成对委托人的代理.

3.2.3 动态代理总结

JDK动态代理和CGLIB动态代理是Java中实现动态代理的两种常用机制,虽然它们都可以为目标对象创建代理对象并拦截方法调用,但它们的工作原理和使用场景有所不同。以下是它们的区别:

1. 基于接口 vs 基于类继承
  • JDK动态代理

    • 基于接口:JDK动态代理要求被代理的类必须实现一个或多个接口。如果类没有实现任何接口,JDK动态代理将无法工作。

    • 优点:实现简单,使用Java内置API、无需依赖第三方库。

    • 缺点:只能代理接口,不能代理普通类。方法调用时使用反射,性能相对较低。

  • CGLIB动态代理

    • 基于类集成:CGLIB动态代理通过生成目标类的子类来实现代理。它不要求目标类必须实现接口,因此它适用于没有实现接口的类。

    • 优点:可以代理没有接口的类。方法调用性能较高,避免了反射调用。

    • 缺点:创建代理类时需要进行字节码操作,性能开销较大。需要依赖cglib和ASM库。

2. 实现机制
  • JDK动态代理

    • 使用反射机制,通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实现代理。代理对象仅代理接口中的方法。

    • 当调用代理对象的方法时,代理类会拦截方法调用,并通过 InvocationHandler.invoke() 方法执行额外的逻辑。

  • CGLIB动态代理

    • 基于字节码操作,使用 CGLIB(Code Generation Library)生成目标类的子类并重写目标类的方法来实现代理。通过继承方式拦截所有非 final 方法的调用。

    • CGLIB 使用的是 ASM 字节码生成框架,生成的是字节码级别的代理类,因此性能相对较好,但生成代理类的开销比JDK动态代理略大。

3. 性能差异
  • JDK动态代理

    • 对于实现了接口的类来说,JDK动态代理在创建代理对象时开销较小,因为它仅依赖反射机制来处理接口方法的调用。

    • 对于频繁调用代理方法的场景,JDK动态代理可能比CGLIB略慢,因为每次调用都涉及反射。

  • CGLIB动态代理

    • 由于CGLIB是通过字节码生成来创建代理类,生成代理类的开销比JDK动态代理高一些,尤其是在代理类较多的情况下。

    • 但CGLIB代理的实际方法调用性能更高,因为它通过字节码操作,减少了反射调用的开销。

4. 使用场景
  • JDK动态代理

    • 适用于接口驱动的编程,如果目标类实现了接口,那么使用JDK动态代理是首选方式。

    • 适合在不需要对类进行直接代理的场景,通常在应用中,业务逻辑往往是通过接口定义的,因此JDK代理在实际项目中更常用。

  • CGLIB动态代理

    • 适用于没有实现接口的类,例如一些现有类或者第三方库的类没有提供接口的情况下,可以使用CGLIB动态代理。

    • 适用于对类进行代理时,但需要注意类不能是 final,否则CGLIB无法生成代理子类。

5. Spring AOP中的使用
  • JDK动态代理

    • 在Spring AOP中,如果目标对象实现了接口,Spring默认使用JDK动态代理。这是因为Spring AOP的核心思想是基于接口的面向切面编程(Aspect-Oriented Programming)。

  • CGLIB动态代理

    • 如果目标对象没有实现任何接口,Spring AOP会自动使用CGLIB动态代理。在Spring配置中,你也可以强制使用CGLIB代理(通过设置 proxyTargetClass=true)。


http://www.ppmy.cn/news/1564422.html

相关文章

iOS - 关联对象的实现

根据源码总结一下关联对象(Associated Objects)的实现&#xff1a; 1. 关联对象的基本结构 // 对象的 isa 结构中包含关联对象标记 union isa_t {struct {uintptr_t nonpointer : 1; // 是否使用优化的 isauintptr_t has_assoc : 1; // 是否有关联对象// ...其他位…

掌握 React 高阶组件与高阶函数:构建可复用组件的新境界

一、引言 在 React 开发中&#xff0c;代码复用性和逻辑分离是提高开发效率和维护性的重要手段。高阶组件&#xff08;Higher-Order Component, HOC&#xff09;和高阶函数&#xff08;Higher-Order Function, HOF&#xff09;是实现这一目标的两种强大工具。本文将详细介绍这…

隧道IP广播与紧急电话系统:提升隧道安全的关键技术

隧道IP广播与紧急电话系统&#xff1a;提升隧道安全的关键技术 随着现代城市交通的迅猛发展&#xff0c;隧道作为重要的交通基础设施&#xff0c;其安全性与应急处理能力显得尤为重要。隧道IP广播与紧急电话系统作为保障隧道安全的关键技术&#xff0c;正发挥着越来越重要的作…

HarmonyOS使用Grid网格实现计算器功能实现

使用Grid网格处理&#xff0c;实现了计算器的加减乘除功能 Entry Component struct GridPage {State str: string ""; //暂存区State num: string "0"; //输入区State flagNum: boolean false; //标识build() {Column() {Grid() {GridItem() {Text(this…

WPS excel使用宏编辑器合并 Sheet工作表

使用excel自带的工具合并Sheet表&#xff0c;我们会发现需要开通WPS会员才能使用合并功能&#xff1b; 那么WPS excel如何使用宏编辑器进行合并 Sheet表呢&#xff1f; 1、首先我们要看excel后缀是 .xlsx 还是 .xls &#xff1b;如果是.xlsx 那么 我们需要修改为 .xls 注…

在 Azure 100 学生订阅中新建 Ubuntu VPS 并部署 Mastodon 服务器

今天想和大家分享一下如何在 Azure 的 100 学生订阅中&#xff0c;创建一台 Ubuntu VPS&#xff0c;并通过 Docker 部署 Mastodon 服务器。Mastodon 是一个开源的社交网络平台&#xff0c;允许用户创建自己的实例&#xff0c;类似于 Twitter&#xff0c;但更加去中心化。Docker…

计算机网络 (45)动态主机配置协议DHCP

前言 计算机网络中的动态主机配置协议&#xff08;DHCP&#xff0c;Dynamic Host Configuration Protocol&#xff09;是一种网络管理协议&#xff0c;主要用于自动分配IP地址和其他网络配置参数给连接到网络的设备。 一、基本概念 定义&#xff1a;DHCP是一种网络协议&#xf…

FPGA车牌识别

基于FPGA的车牌识别主要包含以下几个步骤&#xff1a;图像采集、颜色空间转换、边缘检测、形态学处理&#xff08;腐蚀和膨胀&#xff09;、特征值提取、模板匹配、结果显示。先用matlab对原理进行仿真&#xff0c;后用vivado和modelsim进行设计和仿真。 一、1.图像采集采用ov…