Java 代理模式:从静态代理到动态代理

前言

        代理模式是 Java 中常见的设计模式之一,它的核心思想是通过一个代理对象来控制对真实对象的访问。代理模式不仅可以扩展目标对象的功能,而且在不修改原目标对象的情况下,可以增加一些我们自定义的操作。

1. 代理模式简介

        代理模式的核心思想是:通过代理对象来代替对真实对象的访问。这样做的好处是,我们可以在不修改原目标对象的前提下,扩展目标对象的功能。比如,在目标对象的某个方法执行前后,你可以增加一些自定义的操作。

举个例子:

新娘找来了自己的闺蜜来代替自己处理新郎的提问。新娘收到的提问都是经过闺蜜处理过滤之后的。闺蜜在这里就可以看作是新娘的代理对象,代理的行为(方法)是接收和回复新郎的提问。

再举个例子:

你是一个明星,每天有很多粉丝找你签名。但你是大忙人,没时间亲自处理这些请求,所以你找了一个经纪人(代理对象)来帮你处理。粉丝的请求先交给经纪人,经纪人再决定是否转交给你,或者帮你做一些额外的处理(比如记录谁来找你签名)。这样,你既不用亲自处理所有请求,又能保证签名工作顺利进行。

2. 静态代理

2.1 什么是静态代理?

        静态代理是指,在编译时就已经确定了代理类和目标类的关系。我们需要手动为每个目标类创建一个代理类,并在代理类中调用目标类的方法。

        静态代理的缺点:灵活性较差,一旦接口新增方法,目标类和代理类都需要进行修改。

2.2 静态代理的实现步骤

  1. 定义一个接口及其实现类
  2. 创建一个代理类并实现该接口
  3. 将目标对象注入代理类,并在代理类的方法中调用目标类的方法

2.3 代码示例

1. 定义发送短信的接口
public interface SmsService {String send(String message);
}
2. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {public String send(String message) {System.out.println("send message:" + message);return message;}
}
3. 创建代理类并实现发送短信的接口
public class SmsProxy implements SmsService {private final SmsService smsService;public SmsProxy(SmsService smsService) {this.smsService = smsService;}@Overridepublic String send(String message) {// 调用方法之前,我们可以添加自己的操作System.out.println("before method send()");smsService.send(message);// 调用方法之后,我们同样可以添加自己的操作System.out.println("after method send()");return null;}
}
4. 实际使用
public class Main {public static void main(String[] args) {SmsService smsService = new SmsServiceImpl();SmsProxy smsProxy = new SmsProxy(smsService);smsProxy.send("java");}
}

运行上述代码之后,控制台打印出:

before method send()
send message:java
after method send()

可以看到,我们成功地在 SmsServiceImpl 的 send() 方法前后增加了自定义操作。

3. 动态代理

3.1 什么是动态代理?

        相比于静态代理,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,而是可以在运行时动态生成代理类。动态代理的实现方式有很多种,比如 JDK 动态代理和 CGLIB 动态代理。

3.2 JDK 动态代理

3.2.1 JDK 动态代理的核心

        在 JDK 动态代理中,InvocationHandler 接口和 Proxy 类是核心。Proxy 类的 newProxyInstance() 方法用于生成代理对象,而 InvocationHandler 接口的 invoke() 方法则用于处理代理对象的方法调用。

3.2.2 JDK 动态代理的实现步骤
  1. 定义一个接口及其实现类
  2. 自定义 InvocationHandler 并重写 invoke 方法
  3. 通过 Proxy.newProxyInstance() 方法创建代理对象
3.2.3 代码示例
1. 定义发送短信的接口
public interface SmsService {String send(String message);
}
2. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {public String send(String message) {System.out.println("send message:" + message);return message;}
}
3. 定义一个 JDK 动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class DebugInvocationHandler implements InvocationHandler {private final Object target;public DebugInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before method " + method.getName());Object result = method.invoke(target, args);System.out.println("after method " + method.getName());return result;}
}
4. 获取代理对象的工厂类
import java.lang.reflect.Proxy;public class JdkProxyFactory {public static Object getProxy(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new DebugInvocationHandler(target));}
}
5. 实际使用
public class Main {public static void main(String[] args) {SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());smsService.send("java");}
}

运行上述代码之后,控制台打印出:

before method send
send message:java
after method send

3.3 CGLIB 动态代理

3.3.1 CGLIB 动态代理的核心

        CGLIB 动态代理通过继承目标类来生成代理类,因此它可以代理未实现任何接口的类。CGLIB 的核心是 MethodInterceptor 接口和 Enhancer 类。

3.3.2 CGLIB 动态代理的实现步骤
  1. 定义一个类
  2. 自定义 MethodInterceptor 并重写 intercept 方法
  3. 通过 Enhancer 类的 create() 方法创建代理类
3.3.3 代码示例
1. 实现一个使用阿里云发送短信的类
public class AliSmsService {public String send(String message) {System.out.println("send message:" + message);return message;}
}
2. 自定义 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class DebugMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("before method " + method.getName());Object object = methodProxy.invokeSuper(o, args);System.out.println("after method " + method.getName());return object;}
}
3. 获取代理类
import net.sf.cglib.proxy.Enhancer;public class CglibProxyFactory {public static Object getProxy(Class<?> clazz) {Enhancer enhancer = new Enhancer();enhancer.setClassLoader(clazz.getClassLoader());enhancer.setSuperclass(clazz);enhancer.setCallback(new DebugMethodInterceptor());return enhancer.create();}
}
4. 实际使用
public class Main {public static void main(String[] args) {AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);aliSmsService.send("java");}
}

运行上述代码之后,控制台打印出:

before method send
send message:java
after method send

4. 静态代理和动态代理的对比

  • 灵活性:动态代理更加灵活,不需要针对每个目标类都创建一个代理类,且可以直接代理实现类。
  • JVM 层面:静态代理在编译时生成 class 文件,而动态代理在运行时动态生成类字节码。

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

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

相关文章

PyCharm 2019.1.3使用python3.9创建虚拟环境setuptools-40.8.0报错处理

目录 前置&#xff1a; 一劳永逸方法&#xff08;缺最后一步&#xff0c;没有成行&#xff09; step one: 下载高版本的pip、setuptools、virtualenv的tar.gz包 step two: 进入PyCharm安装目录的 helpers 目录下 step three: 下载并安装grep和sed命令&#xff0c;然后执行 …

word处理控件Aspose.Words教程:使用 Python 删除 Word 中的空白页

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。 Aspose API支持流行文件格式处理&#xff0c;并…

C++数据结构1——栈结构详解

一、栈的基本概念与特性 1. 栈的定义与特点 栈&#xff08;Stack&#xff09;是一种遵循后进先出&#xff08;LIFO, Last In First Out&#xff09;原则的线性数据结构&#xff0c;其核心特征包括&#xff1a; 单端操作&#xff1a;所有操作仅通过栈顶进行 动态存储&#xf…

77.HarmonyOS NEXT ImageViewerView 组件深度剖析: Swiper容器与懒加载深度解析

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT ImageViewerView 组件深度剖析&#xff1a; Swiper容器与懒加载深度解析 一、组件基础结构 Component export struct ImageViewe…

向量数据库对比以及Chroma操作

一、向量数据库与传统类型数据库 向量数据库&#xff08;Vector Storage Engine&#xff09;与传统类型的数据库如关系型数据库&#xff08;MySQL&#xff09;、文档型数据库&#xff08;MongoDB&#xff09;、键值存储&#xff08;Redis&#xff09;、全文搜索引擎&#xff0…

深入解析对象存储及工作原理

在现代信息技术发展中&#xff0c;存储是一个永恒的话题。从最初的磁带、硬盘到现在的云存储&#xff0c;存储技术不断推陈出新。而其中&#xff0c;“对象存储”作为近年来备受关注的存储技术之一&#xff0c;凭借其高可扩展性和灵活性&#xff0c;逐渐成为企业级存储方案的首…

ctfshow-xxs-316-333-wp

316.反射型 XSS&#xff08;-326都是反射型&#xff09; js恶意代码是存在于某个参数中&#xff0c;通过url后缀进行get传入&#xff0c;当其他用户点进这个被精心构造的url链接时&#xff0c;恶意代码就会被解析&#xff0c;从而盗取用户信息。 来看题&#xff0c;先简单测试…

easypoi导入Excel兼容日期和字符串格式的日期和时间

问题场景 在使用easypoi导入Excel时&#xff0c;涉及到的常用日期会有yyyy-MM-dd HH:mm:ss、yyyy-MM-dd和HH:mm:ss&#xff0c;但是Excel上面的格式可不止这些&#xff0c;用户总会输入一些其他格式&#xff0c;如 如果在定义verify时用下面这种格式定义&#xff0c;那么总会…

基于yolo11+flask打造一个精美登录界面和检测系统

这个是使用flask实现好看登录界面和友好的检测界面实现yolov11推理和展示&#xff0c;代码仅仅有2个html文件和一个python文件&#xff0c;真正做到了用最简洁的代码实现复杂功能。 测试通过环境&#xff1a; windows x64 anaconda3python3.8 ultralytics8.3.81 flask1.1.…

R语言零基础系列教程-01-R语言初识与学习路线

代码、讲义、软件回复【R语言01】获取。 R语言初识 R是一个开放的统计编程环境&#xff0c;是一门用于统计计算和作图的语言。“一切皆是对象”&#xff0c;数据、函数、运算符、环境等等都是对象。易学&#xff0c;代码像伪代码一样简洁&#xff0c;可读性高强大的统计和可视…

AI重塑视觉艺术:DeepSeek与蓝耘通义万相2.1的图生视频奇迹

云边有个稻草人-CSDN博客 近年来&#xff0c;深度学习、计算机视觉和生成模型在多个领域取得了突破性进展。其中&#xff0c;DeepSeek与蓝耘通义万相2.1图生视频的结合为图像生成与视频生成技术提供了新的发展方向。DeepSeek作为一个图像和视频生成的工具&#xff0c;能够利用深…

ELK+Filebeat+Kafka+Zookeeper安装部署

1.安装zookeeper zookpeer下载地址:apache-zookeeper-3.7.1-bin.tar.gzhttps://link.csdn.net/?targethttps%3A%2F%2Fwww.apache.org%2Fdyn%2Fcloser.lua%2Fzookeeper%2Fzookeeper-3.7.1%2Fapache-zookeeper-3.7.1-bin.tar.gz%3Flogin%3Dfrom_csdn 1.1解压安装zookeeper软件…

历年云南大学计算机复试上机真题

历年云南大学计算机复试机试真题 在线评测&#xff1a;传送门&#xff1a;pgcode.cn 喝饮料 题目描述 商店里有 n 中饮料&#xff0c;第 i 种饮料有 mi 毫升&#xff0c;价格为 wi。 小明现在手里有 x 元&#xff0c;他想吃尽量多的饮料&#xff0c;于是向你寻求帮助&#x…

怎么有效降低知网AIGC率

在学术创作日益规范且数字化检测技术不断发展的当下&#xff0c;知网 AIGC 检测成为了众多创作者关注的焦点。许多人苦恼于如何有效降低知网 AIGC 率&#xff0c;让自己的作品在通过检测的同时&#xff0c;彰显出真实的创作水平与独特性。接下来&#xff0c;我们就深入探讨降低…

代码随想录day17 二叉树part05

654.最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums …

【Python入门】一篇掌握Python中的字典(创建、访问、修改、字典方法)【详细版】

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;《Python/PyTorch极简课》_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目…

LeetCode 环形链表II:为什么双指针第二次会在环的入口相遇?

快慢指针 为什么相遇后让快指针回到起点&#xff0c;再让快指针和慢指针都一步一步地走&#xff0c;它们就会在环的入口相遇&#xff1f; 复杂度 时间复杂度: O(n) 空间复杂度: O(1) public ListNode detectCycle(ListNode head) {ListNode slow head, fast head;ListNode …

HarmonyOS第24天:鸿蒙应用安全秘籍:如何为用户数据筑牢防线?

开篇引入 在数字化时代&#xff0c;我们的生活越来越依赖各种应用程序。从社交娱乐到移动支付&#xff0c;从健康管理到工作学习&#xff0c;应用已经渗透到生活的方方面面。然而&#xff0c;随着应用使用的日益频繁&#xff0c;用户隐私数据泄露的风险也在不断增加。 前几年&…

P2730 魔板 (写了巨久..有一些数字,字符,字符串之间的转换规则)

ac代码&#xff1a; #include<iostream> #include<map> #include<queue> using namespace std; map<string,int>mp1,mp2; map<string,string>mp3; queue<string>q; string str,res"12345678"; void pri(string str){if(resstr)…

Centos7使用docker搭建redis集群

前置准备&#xff1a; Centos7安装docker就不多说了… 本次目的是搭建3主3从&#xff08;当然你也可以按需扩展&#xff09;准备三台服务器&#xff0c;假定IP分别为&#xff1a;192.168.75.128、192.168.75.129、192.168.75.130安装 redis&#xff1a; #拉取redis docker p…