10.结构型 - 代理模式 (Proxy Pattern)

news/2025/12/3 22:07:07/文章来源:https://www.cnblogs.com/dddy/p/19304154

代理模式 (Proxy Pattern)

在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式.

代理模式(Proxy Design Pattern ) 原始定义是:让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问,并允许将请求提交给对象前后进行一些处理。

静态代理

静态代理实现

//接口类: IUserDao <抽象目标类>
public interface IUserDao {void save();
}
//目标对象:UserDaoImpl <目标角色(被代理)>
public class UserDaoImpl implements IUserDao {@Overridepublic void save() {System.out.println("保存数据");}
}//静态代理对象:UserDaoProxy 需要实现IUserDao接口 <代理角色>
public class UserDaoProxy implements IUserDao {private IUserDao target;public UserDaoProxy(IUserDao target) {this.target = target;}@Overridepublic void save() {System.out.println("开启事务"); //扩展额外功能target.save();System.out.println("提交事务");}
}//测试类
public class TestProxy {@Testpublic void testStaticProxy(){//目标对象UserDaoImpl userDao = new UserDaoImpl();//代理对象UserDaoProxy proxy = new UserDaoProxy(userDao);proxy.save();}
}

其实就是编码实现, 通过组合或继承持有原始类 再扩展它的方法

动态代理

动态代理: 动态地在内存中构建代理对象, 从而实现对目标对象的代理功能.

静态代理与动态代理的区别:

  1. 静态代理在编译时就已经实现了,编译完成后代理类是一个实际的class文件
  2. 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件, 而是在运行时动态生成类字节码,并加载到JVM中.

Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载阶段需要完成以下3件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据访问入口

动态代理就是想办法,根据接口或目标对象,动态生成代理类的字节码,然后再加载到JVM中使用

JDK动态代理实现

package org.yang.mca.pattern10;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JDKProxyFactory {private Object target; //维护一个目标对象public JDKProxyFactory(Object target) {this.target = target;}//为目标对象生成代理对象public Object getProxyInstance(){//使用Proxy获取代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(), //目标类使用的类加载器target.getClass().getInterfaces(), //目标对象实现的接口类型, 必须有一个接口new InvocationHandler(){ //事件处理器 (调用拦截)/*** invoke方法参数说明* @param proxy 代理对象* @param method 对应于在代理对象上调用的接口方法Method实例* @param args 代理对象调用接口方法时传递的实际参数* @return: java.lang.Object 返回目标对象方法的返回值,没有返回值就返回null*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK 代理拦截,调用前, "+method.getName());//执行目标对象方法Object ret = method.invoke(target, args);System.out.println("JDK 代理拦截,调用后, "+method.getName());return ret;}});}
}

测试

@Test  
public void testJDKProxy() {  IUserDao target = new UserDaoImpl();  System.out.println("目标对象信息 :"+ target.getClass());//目标对象信息  IUserDao proxy = (IUserDao) new JDKProxyFactory(target).getProxyInstance();  System.out.println("代理对象信息 :"+ proxy.getClass());//输出代理对象信息  proxy.save(); //执行代理方法  // while (true){ }  
}

JDK 动态代理实现原理

使用阿里开源的 arthas 工具, jad 命令可以 直接反编译出class的源码

java -jar arthas-boot.jar
jad jdk.proxy2.$Proxy7

精简后大概代码

package jdk.proxy2;import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.yang.mca.pattern10.IUserDao;public final class $Proxy7
extends Proxy
implements IUserDao {//注意 JDK动态代理是基于接口, 没有接口是不行滴private static final Method m0;private static final Method m1;private static final Method m2;private static final Method m3;public $Proxy7(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m3 = Class.forName("org.yang.mca.pattern10.IUserDao").getMethod("save", new Class[0]);return;}}public final void save() {try {this.h.invoke(this, m3, null);return;}}
....
}

cglib 动态代理实现

cglib代理类,需要实现MethodInterceptor接口,并指定代理目标类target

CglibDynamicProxy.java

package org.yang.mca.pattern10;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;/*** @author yangfh* @date 2023-11-23 17:45**/
public class CglibDynamicProxy implements MethodInterceptor {private Object target;public CglibDynamicProxy(Object target) {this.target = target;}public Object getProxy(){//增强器类,用来创建动态代理类Enhancer en = new Enhancer();//设置代理类的父类字节码对象en.setSuperclass(target.getClass());//设置回调: 对于代理类上所有的方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截en.setCallback(this);//创建动态代理对象并返回return en.create();}/*** 实现回调方法* @param o     代理对象* @param method  目标对象中的方法的Method实例* @param args      实际参数* @param methodProxy  代理对象中的方法的method实例* @return: java.lang.Object*/@Overridepublic Object intercept(Object o, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {Calendar calendar = Calendar.getInstance();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(formatter.format(calendar.getTime()) + " [" +method.getName() + "]   CglibDynamicProxy 调用拦截]");Object result = methodProxy.invokeSuper(o, args);return result;}
}

测试:

//测试Cglib 实现 动态代理  
public static void main(String[] args) {  //目标对象  IUserDao target = new UserDaoImpl();  System.out.println("目标对象信息 :"+ target.getClass());//目标对象信息  //代理对象  CglibDynamicProxy dynamicProxy = new CglibDynamicProxy(target);  IUserDao proxy = (IUserDao) dynamicProxy.getProxy();  System.out.println("代理对象信息 :"+ proxy.getClass());//输出代理对象信息  proxy.save(); //执行代理方法  }

jdk9 以上需要 open module 参数 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED

cglib 动态代理实现原理

反编译后的源码

public class UserDaoImpl$$EnhancerByCGLIB$$2d13c5a
extends UserDaoImpl
{
....}

CGLIB 底层基于 ASM 操作字节码, 相对与JDK只能对接口进行代理, 它通过继承的方式, 可以对任意对象进行代理; (所以被 final 修饰的类, CGLIB是无能为力的)

  • 最底层是字节码
  • ASM是操作字节码的工具
  • cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)

in short 通过继承的方式创建代理对象, 原理是: cglib -> ASM -> 字节码

代理模式总结

关键角色

代理(Proxy)模式分为三种角色:

  1. 目标角色
  2. 代理角色
  3. 应该面向抽象编程, 所以还有一个(抽象目标角色)

JDK代理和CGLIB代理

使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。
唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。

在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。

动态代理和静态代理

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

代理模式优缺点

优点:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

缺点:

  • 增加了系统的复杂度;
  • 如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,增加了代码维护的复杂度;

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

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

相关文章

敏捷冲刺日志 - Day 1

敏捷冲刺日志 - Day 1 各个成员在 Alpha 阶段认领的任务 本次七天冲刺即为项目的 Alpha 阶段,目标是交付一个具备核心功能、可运行、可演示的最小可行产品(MVP)。团队成员在本阶段的任务分配如下:刘瑞康 (开发):负…

2025年中国集成灶十大品牌综合实力榜:选购指南与权威解析

body { font-family: "Microsoft YaHei", sans-serif; line-height: 1.8; color: rgba(51, 51, 51, 1); max-width: 1000px; margin: 0 auto; padding: 20px; background-color: rgba(249, 249, 249, 1) } h…

朝花夕拾OI回忆录

朝花夕拾 OI 回忆录 序言 或许是因为喜欢追忆吧,也或许是临近AFO,内心有一些触动,又或者是为了给后续的OIer一些前者的失败经验吧……总之,2025年12月3日,我十六岁生日这天,我决定写这篇 OI 回忆录,以记录我对O…

细胞因子:细胞信使的分子世界与功能解析

在复杂的多细胞生物体内,细胞间的信息交流是维持生命活动的基础。其中,细胞因子 作为一类关键的信使分子,在免疫调节、细胞生长、分化、炎症反应和组织修复等过程中扮演着不可或缺的角色。本文将深入探讨细胞因子的…

NOIp 的 p 是 painting 的 p!

哇还有连续剧。 作者在 CSP 后推完魔宴正在推 WA2。 Day -6 发现惊天理论:Day -3 最后的 ZR 有点娱乐赛,T1 是哈希表广告题,赛时裸 umap 拿了 90pts,赛后拿 umap 和 gp 卡了一万年卡不过,严肃学习 Dzb 牌哈希表,…

AWS云计算入门指南:从零到一,详解核心服务与免费套餐 - 教程

AWS云计算入门指南:从零到一,详解核心服务与免费套餐 - 教程2025-12-03 22:00 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !impor…

概率论直觉(二):方差与期望 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

图书馆管理系统项目冲刺 Day7

图书馆管理系统项目冲刺 Day7 一、站立式会议 照片: [插入Day6站立会议照片] 二、昨天已完成的工作 读者管理和个人中心完成 搜索优化和推荐系统实现 统计分析功能添加 三、今天计划完成的工作 何昊天: 实现系统设置…

就想赚点学分有什么不队-团队第三次作业—alpha冲刺

就想赚点学分有什么不队-团队第三次作业—alpha冲刺就想讨点学分有什么不队-冲刺总结 一、作业基本信息项目 内容这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/202501SoftwareEngineering/这个作业要求在…

CSAPP 优化程序性能

目录优化程序性能程序剖析程序优化的基本原则编译器级别的优化指定优化等级函数内联优化编译器级别优化的局限性程序的性能程序性能的衡量对现代处理器的理解功能单元的性能代码级别的优化减少重复的运算和调用代码移动…

87键键盘的数字键对应快捷键含义

针对87键键盘的数字键对应的快捷键含义参考如下图:【参考】 VGN V98 Pro键盘使用说明作者:编程随笔 出处:http://www.cnblogs.com/nuccch/ 声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给…

深入解析:Python异步(Asyncio)(一)

深入解析:Python异步(Asyncio)(一)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

Java控制流程

Java流程控制 用户交互ScannerScanner工具类:获取用户的输入 基本语法:Scanner s = new Scanner(System.in);通过Scanner类的next()与nextLine()方法获取输入的字符串 在读取之前一般需要使用hasNext()与hasNextLine(…

快速判断是数字字符还是英文字符

问:(c >> 6 & 1) * 2 - 1是什么意思? 答:对于任意小写/大写英文字母字符,其 ASCII 码的二进制都形如 01xxxxxx;对于任意数字字符,其 ASCII 码的二进制都形如 0011xxxx。 根据这一特点,可以根据二进制…

2025最新成都旧房装修改造实力品牌推荐!家装标杆企业榜单发布,专业焕新服务助力品质生活升级

随着存量房时代到来,旧房装修改造成为家庭改善居住品质的核心需求。本榜单基于施工标准、环保工艺、空间规划、售后保障四大维度,结合成都市建筑装饰协会2024年度行业数据及业主满意度调研,权威解析2025年成都五大旧…

深入解析:神经流形:大脑功能几何基础的革命性视角

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025最新成都房屋装修公司推荐!成都家装市场权威榜单发布,品质服务双优助力打造理想家居

随着人们对居住品质要求的不断提升,房屋装修行业迎来新的发展机遇。本榜单基于设计实力、施工质量、材料环保性、服务体验四大维度,结合行业客户反馈及第三方评估数据,权威解析2025年成都五大房屋装修品牌综合实力,…

P6_MMSegmentation训练语义分割深度学习算法

P6_MMSegmentation训练语义分割深度学习算法6.1【G】训练语义分割模型 1.跑通代码 python tools/train.py Zihao-Configs/ZihaoDataset_FastSCNN_20230818.py 由于jupyter演示的代码模型太大,换了轻量级的FastSCNN_20…

2025.12.3

今天学习了vue的一些常用组件