1. 代理模式介绍

代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象的访问。代理对象通常在客户端和目标对象之间起到中介作用,能够在不改变目标对象的前提下增加额外的功能操作,比如延迟初始化、访问控制、日志记录等。

通俗来说,代理模式就像是你在现实生活中使用中介服务一样。比如租房时,你可能不会直接联系房东,而是通过房屋中介来完成租赁过程。中介(代理)会帮你处理一些细节问题,如看房、谈判价格等,最终让你顺利租到房子。

代理模式有四种常见实现方式:

实现方式核心机制依赖要求限制条件性能灵活性代码侵入性
基于接口的静态代理手动创建代理类,实现相同接口依赖接口必须实现接口★★★★☆ (高)★★☆☆☆ (低)
基于继承的静态代理代理类继承目标类并重写方法不需要接口不能代理final类/方法★★★★☆ (高)★★☆☆☆ (低)
基于JDK的动态代理Proxy+InvocationHandler反射生成字节码必须实现接口JDK原生支持,要求有接口★★★☆☆ (中等)★★★★★ (高)
基于CGLIB的动态代理继承目标类+MethodInterceptor修改字节码不需要接口不能代理final类/方法;需第三方库★★★★☆ (较高)★★★★★ (高)

2.代码演示

2.1 基于接口的静态代理

实现原理

  • 代理类与目标类实现相同接口
  • 代理类持有目标对象的引用
  • 手动在接口方法中添加增强逻辑

示例场景

//接口
public interface Subject {void request();
}//被代理的目标类
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: Handling request.");}
}//“基于接口”的静态代理类
public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic void request() {if (realSubject == null) {realSubject = new RealSubject();}System.out.println("ProxySubject: Pre-processing before handling request.");realSubject.request();System.out.println("ProxySubject: Post-processing after handling request.");}
}

使用方式:

public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing ProxySubject:");Subject proxySubject = new ProxySubject();proxySubject.request();}
}

2.2 基于继承的静态代理

实现原理

  • 代理类继承目标类
  • 重写方法并添加增强逻辑
  • 通过super调用父类方法

示例场景

//被代理的目标类
public class RealSubject {public void request() {System.out.println("RealSubject: Handling request.");}
}//“基于继承”的静态代理方式
public class ProxySubjectByInheritance extends RealSubject {@Overridepublic void request() {System.out.println("ProxySubjectByInheritance: Pre-processing before handling request.");super.request();System.out.println("ProxySubjectByInheritance: Post-processing after handling request.");}
}

使用方式:

public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing ProxySubjectByInheritance:");Subject proxySubjectByInheritance = new ProxySubjectByInheritance();proxySubjectByInheritance.request();}
}

2.3 基于JDK的动态代理

实现原理

  • 底层使用Java的反射机制
  • 运行时通过java.lang.reflect.Proxy.newProxyInstance()动态生成代理对象
  • 通过InvocationHandler统一处理所有方法调用

示例场景

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** “基于JDK”的动态代理(可以看作是“基于接口”的动态代理)* 要求: 代理的目标对象必须实现接口* 提供者: JDK,使用JDK官方的Proxy类创建代理对象* 代理类: 生成的代理类和被代理类基于相同的接口,只能使用用接口里的public方法**/
public class JdkProxySubject implements InvocationHandler {private Object target;/*** 创建代理对象的方法** @param target 被代理的原始对象; 被代理对象的类必须实现接口* @return 代理对象; 与被代理对象实现相同接口*/public Object getInstance(Object target) {this.target = target;// 创建对象的代理对象// 参数1:类加载器; 参数2:被代理对象的接口; 参数3:代理类return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}/*** 拦截所有目标类的方法** @param proxy  目标类对象* @param method 目标类的方法* @param args   目标类的方法参数* @return 目标类方法的返回值* @throws Throwable 目标类方法抛出的异常*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JdkProxySubject: Pre-processing before handling request.");Object result = method.invoke(target, args);System.out.println("JdkProxySubject: Post-processing after handling request.");return result;}
}

使用方式:

public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing JdkProxySubject:");JdkProxySubject jdkProxySubject = new JdkProxySubject();Subject realSubjectProxy = (Subject) jdkProxySubject.getInstance(new RealSubject());realSubjectProxy.request();}
}

2.4 基于CGLIB的动态代理

实现原理

  • 使用CGLIB库来生成代理类,这个库允许我们在运行时动态地创建目标类的子类
  • 采用方法拦截(MethodInterceptor)机制
  • 通过FastClass机制避免反射调用

示例场景

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;/*** “基于CGLB”的动态代理(可以看作“基于继承”的动态代理)* 提供者: 第三方 CGLib,使用CGLib的Enhancer类创建代理对象* 代理类: 生成的代理类是被代理类的子类,可以使用被代理类中的 public 和 protected 方法*/
public class CglibProxySubject implements MethodInterceptor {private Object target;/*** 使用CGLib创建动态代理对象** @param target 被代理的原始对象; 被代理对象不能用final修饰(final修饰后无法继承了)* @return 代理对象; 是被代理对象的子类*/public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());enhancer.setCallback(this);return enhancer.create();}/*** 拦截所有目标类的方法** @param obj    目标类的实例* @param method 目标类的方法* @param args   目标类方法的参数* @param proxy  目标类方法的代理* @return 目标类方法的返回值* @throws Throwable 目标类方法抛出的异常*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("CglibProxySubject: Pre-processing before handling request.");Object result = proxy.invokeSuper(obj, args);System.out.println("CglibProxySubject: Post-processing after handling request.");return result;}
}

使用方式:

public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing CglibProxySubject:");CglibProxySubject cglibProxySubject = new CglibProxySubject();RealSubject realSubjectCglibProxy = (RealSubject) cglibProxySubject.getInstance(new RealSubject());realSubjectCglibProxy.request();}
}

3.四种代理模式比较

以下是四种代理模式的对比分析及使用场景总结:

代理方式优点缺点适用场景
基于接口的静态代理

1.结构清晰,易于理解和维护

2.性能最优(无反射开销),类型安全

1.需要为每个被代理类编写代理类,代码冗余

2.类方法变更需同步修改代理类

1.接口方法数量少且稳定

2.需要极致性能的场景(如高频调用)或者需要精确控制代理逻辑的场景

3.简单项目或教学演示

基于继承的静态代理1.可以代理没有接口的类

1.同样需要编写大量代码;

2.不能代理final类/方法

3.类方法变更需修改代理类

1.遗留系统改造(无接口的类)

2.需要代理第三方类库(拿不到三方类库的接口)

3.不涉及final修饰的类/方法

JDK动态代理

1.无需编写代理类(动态生成),自动适配所有接口方法,减少代码量;

2. 支持统一拦截逻辑

1.只能代理实现了接口的类;

2.反射调用性能略低

1.Spring AOP默认实现

2.需要代理接口多个方法的场景

3.代理逻辑复杂且需复用的系统(如日志/事务统一管理)

CGLIB动态代理

1.可以代理没有接口的类;

2.性能通常优于JDK动态代理,接近静态代理(FastClass机制)

1.不能代理final类或final方法;

2.需引入第三方库(如Spring-core已内置)

3.生成字节码可能增加内存开销

1.代理没有接口的类(Spring对无接口Bean的代理)

2.需要高性能代理的场景(如Hibernate延迟加载)

3.代理非final的第三方类库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914121.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914121.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914121.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言<数据结构-单链表>(收尾)

上篇博客我将基础的尾插、尾删、头插、头删逐一讲解了,这篇博客将对上篇博客进行收尾,讲一下指定位置操作增删以及查找这几个函数,其实大同小异:一.查找函数:查找函数其实就是一个简单的循环遍历,所以不加以…

十年架构心路:从单机到云原生的分布式系统演进史

十年架构心路:从单机到云原生的分布式系统演进史 这里写目录标题十年架构心路:从单机到云原生的分布式系统演进史一、技术生涯的起点:单体架构的黄金时代1.1 典型技术栈1.2 记忆深刻的故障二、分布式架构转型期2.1 服务化拆分实践2.2 分布式事…

使用docker搭建nginx

安装docker 和 docker compose验证docker版本配置docker目录配置代理,使docker能访问外网能否ping通最后直接拉入镜像即可docker pull nginx

Intel新CPU助攻:微软Copilot+将登陆台式电脑

微软的Copilot PC计划已经推出一年多,但目前仅支持平板电脑和笔记本电脑,以及少数迷你电脑。 随着Intel下一代桌面处理器——代号为“Arrow Lake Refresh”的推出,Copilot PC功能有望扩展到桌面计算机。 要支持Copilot PC的所有功能&#xff…

【Kubernetes】跨节点 Pod 网络不通排查案例

最近在部署一个集群环境的时候,发现集群中一个子节点与其他子节点不通,而 master 节点可与任何子节点互通,通过抓包排查后,发现是 Linux 路由决策导致的。因此,在此记录下来,希望对大家有所帮助。1、环境及…

【算法训练营Day11】二叉树part1

文章目录理论基础二叉树的递归遍历前序遍历中序遍历后序遍历总结二叉树的层序遍历基础层序遍历二叉树的右视图理论基础 二叉树在结构上的两个常用类型: 满二叉树完全二叉树 在功能应用上的比较常用的有: 二叉搜索树: 节点有权值、遵循”左…

Flutter 之 table_calendar 控件

1.库导入在pubspec.yaml文件中dev_dependencies:table_calendar: ^3.2.02. 代码编写TableCalendar(daysOfWeekHeight: 20,availableGestures: AvailableGestures.horizontalSwipe,firstDay: DateTime.now().subtract(const Duration(days: 365)),lastDay: DateTime.now(),cal…

【leetcode】1486. 数组异或操作

数组异或操作题目题解题目 1486. 数组异或操作 给你两个整数,n 和 start 。 数组 nums 定义为:nums[i] start 2*i(下标从 0 开始)且 n nums.length 。 请返回 nums 中所有元素按位异或(XOR)后得到的…

php7.4使用 new DateTime;报错 Class DateTime not found

php7.4使用 new DateTime;报错Uncaught Error: Class ‘app\home\c\DateTime’ not found 查了半天资料,最后找到了解决办法 DateTime 是 php 内置的类,不隶属于任何命名空间,如果你需要在命名空间中使用须有 \ 声明,解决办法就是…

Gartner《构建可扩展数据产品建设框架》心得

一、背景与价值 1.1 “数据产品”为什么忽然重要? 传统模式:业务提出需求 → IT 建数据集 → ETL 管道爆炸 → 维护成本指数级上升。 新范式:把“数据”包装成“产品”,以产品思维迭代演进,强调复用、自助、可扩展。 Gartner 观察到:大量组织把“报表”或“数据仓库”重…

CentOS/RHEL LVM 磁盘扩展完整教程

CentOS/RHEL LVM 磁盘扩展完整教程📝 前言 在Linux系统管理中,磁盘空间不足是经常遇到的问题。特别是在生产环境中,当根分区空间告急时,我们需要通过添加新磁盘来扩展存储空间。本教程将详细介绍如何在CentOS/RHEL系统中使用LVM&a…

LVGL应用和部署(用lua做测试)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】嵌入式产品做好了,下面就是测试和量产了。以按键屏幕的开发模式为例,如果仅仅是简单的功能测试,那还比较好解决&…

phpstudy搭建pikachu

一.启动mysql和nginx服务二.修改靶场文件参数点击管理打开根目录,将下载好的靶场源文件解压到www目录下三.找到此文件用记事本打开四.修改配置文件五.打开浏览器,输入127.0.0.1/pikachu六.按照步骤初始化心得体会:如果mysql启动又立刻停止,大…

【Linux】GDB/CGDB 调试器学习笔记

GDB/CGDB 调试器学习笔记🚀 前言 GDB 是 GNU 项目下功能强大的命令行调试器,适用于 C/C 等多种语言。CGDB 则是在 GDB 之上构建的轻量级 curses 界面,适合喜欢终端操作且习惯 vi 风格的人。一、GDB 入门篇 1. 编译时带调试信息 gcc -g -O0 -W…

链接代理后无法访问网络

路由方向的问题 cmd 输入 route print 查看路由多了一个不是你网络的路由 我的嘎嘎好用直接那都通 route add -p 0.0.0.0 mask 0.0.0.0 0.0.0.0 参考这个 固定ip if是代理链路的 链路口又敏感词这个文章不合规两次评论区问我

day37 早停策略和模型权重的保存

DAY 37 我今天的笔记是用cpu训练的,请自行修改为gpu训练 仍然是循序渐进,先复习之前的代码 import torch import torch.nn as nn import torch.optim as optim from sklearn.datasets import load_iris from sklearn.model_selection import train_test_…

网络爬虫分类全解析

网络爬虫作为数据获取的重要工具,其分类方式多样,不同类型的爬虫在技术实现、应用场景和功能特性上存在显著差异。深入理解这些分类,有助于开发者根据实际需求选择合适的爬虫方案。本文将从技术特性、应用场景和架构设计三个维度,系统介绍网络爬虫的主要分类。 一、按技术…

ECR仓库CloudFormation模板完整指南

概述 本文档详细介绍了一个通用的Amazon ECR(Elastic Container Registry)仓库CloudFormation模板,该模板支持多业务组、参数化配置,并包含完整的安全策略、生命周期管理和监控功能。 模板特性 核心功能 ✅ 支持4个业务组:app、ai、mall、frontend✅ 灵活的服务名手动输…

C++(STL源码刨析/List)

一 List 核心字段和接口1. 节点字段template<class T> struct __list_node {typedef void* void_pointer;void_pointer prev;void_pointer next;T data; }由于 链表 不是连续的内存块&#xff0c;所以对每一个申请到的内存块要进行统一组织&#xff0c;也就是封装成一个类…

苹果App上架流程:不用Mac也可以上架的方法

iOS App 的上架流程一直被认为是门槛最高、流程最繁琐的移动端工作之一。对很多使用 Windows 或 Linux 进行开发的跨平台团队来说&#xff0c;Mac 的缺位更放大了每一步的难度。 在我们近期为一款本地生活类 App 进行 iOS 上架时&#xff0c;团队成员几乎没有配备本地 Mac&…