03-JAVA设计模式-代理模式详解

news2025/3/9 10:32:07

代理模式

什么是代理模式

Java代理模式是一种常用的设计模式,主要用于在不修改现有类代码的情况下,为该类添加一些新的功能或行为。代理模式涉及到一个代理类和一个被代理类(也称为目标对象)。代理类负责控制对目标对象的访问,并可以在访问前后添加一些额外的操作。

核心作用:

  • 通过代理,控制对对象的访问。
  • 可以详细控制访问某个(某类)对象的方法,在调用这个方法茜做前置处理,调用这个方法后做后置处理(即AOP的微观实现)。

核心角色:

  • 抽象角色:定义代理角色和真实角色的公共对外方法
  • 真实角色:实现抽象角色,定义真实角色所需要实现的业务逻辑,供代理角色调用(关注真正的业务逻辑)。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色业务逻辑方法,来实现抽象角色,并可附加自己的操作。

应用场景:

  • 安全代理:屏蔽对真实角色的直接访问
  • 远程代理:通过代理类处理远程方法调用(RMI)
  • 延迟代理:先加载轻量级代理对象,真正需要再加载真实对象。

Java代理模式主要分为两种:静态代理和动态代理。

静态代理

静态代理是在代码中手动定义代理类,代理类与目标对象实现相同的接口,并在代理类中持有目标对象的引用。
当调用代理类的方法时,代理类会在调用目标对象方法的前后添加一些额外的逻辑。

案例

明星个人接活,与代理人接活
明星个人接活:

  1. 面谈
  2. 合同起草
  3. 签字
  4. 前期准备
  5. 唱歌
  6. 收尾款

通过代理人接活:

  1. 面谈
  2. 合同起草
  3. 签字
  4. 前期准备
  5. 唱歌(明星负责)
  6. 收尾款

UML

在这里插入图片描述

1 提供一个抽象角色,定义代理角色和真实角色的公共对外方法。
2 定义真实角色,实现抽象角色的方法(当然真实角色也可以定义一些自己的方法)
3 定义一个代理角色,实现抽象角色方法,通过传入真实角色调用真实角色的方法
4 使用时,创建代理角色及真实角色,通过代理角色调用相应方法

实现代码

Start.java

// 抽象角色
// 明星:具备唱歌能力
public interface Start {
    void sing();
}

RealStart.java

// 真实角色
// * 实现明星唱歌
public class RealStart implements Start{
    @Override
    public void sing() {
        System.out.println("周杰伦唱歌");
    }
}

ProxyStart.java

// 代理明星
// * 通过实现Star方法代理明星唱歌
public class ProxyStart implements Start{
    // 代理明星
    private Start start;

    // 通过构造器传入设置代理明星
    public ProxyStart(Start start) {
        this.start = start;
    }

    // 重写唱歌,代理实现唱歌
    @Override
    public void sing() {
        System.out.print("5. ");
        start.sing();
    }

    public void interview(){
        System.out.println("1. 面谈");
    }

    public void contractDrafting(){
        System.out.println("2. 合同起草");
    }

    public void signature(){
        System.out.println("3. 签字");
    }

    public void preliminaryPreparation(){
        System.out.println("4. 前期准备");
    }

    public void closingPayment(){
        System.out.println("6. 收尾款");
    }
}

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        // 创建真实角色
        RealStart realStart = new RealStart();
        // 创建代理角色
        ProxyStart proxyStart = new ProxyStart(realStart);
        // 方法调用
        // 面谈
        proxyStart.interview();
        // 合同起草
        proxyStart.contractDrafting();
        // 签字
        proxyStart.signature();
        // 前期准备
        proxyStart.preliminaryPreparation();
        // 唱歌
        proxyStart.sing();
        // 收尾款
        proxyStart.closingPayment();
    }
}

执行结果:

在这里插入图片描述

JDK实现动态代理

动态代理是在运行时动态生成代理类。相对于静态代理,将抽象角色(接口)中声明的所有方法都被转移到调用处理器一个集中的地方中处理,这样可以更加灵活和统一的处理众多的方法。

通过JDK提供了java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。

  • java.lang.reflect.Proxy
    作用:动态生成代理类和对象
  • java.lang.reflect.InvocationHandler(处理器接口)
    • 可以通过invoke方法实现对真实角色的代理访问。
    • 每次通过Proxy生成代理类对象时都要指定对应的处理器对象。

UML

在这里插入图片描述

使用JD实现动态代理时,我们需要创建一个实现了InvocationHandler接口的处理器类,并在该类中实现invoke方法。
然后,我们可以使用Proxy.newProxyInstance方法创建一个代理类的实例。
当调用代理类的方法时,实际上会调用处理器类的invoke方法。

代码实现

Start.java

// 抽象角色
interface Start {
    void sing();
}

RealStart.java

// 真实角色
// * 实现明星唱歌
public class RealStart implements Start {
    @Override
    public void sing() {
        System.out.println("周杰伦唱歌");
    }
}

StartHandle.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// JDK 实现动态代理
public class StartHandle implements InvocationHandler {
    // 代理明星
    private Start start;
    // 通过构造器传入设置代理明星
    public StartHandle(Start start) {
        this.start = start;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理方法执行之前");
        System.out.println("1. 面谈");
        System.out.println("2. 合同起草");
        System.out.println("3. 签字");
        System.out.println("4. 前期准备");
        System.out.print("5. ");
        method.invoke(start,args);
        System.out.println("代理方法执行之后");
        System.out.println("6. 收尾款");
        return true;
    }
}

TestClient.java

import java.lang.reflect.Proxy;

// 动态代理测试
public class TestClient {
    public static void main(String[] args) {
        // 创建真实角色
        RealStart realStart = new RealStart();
        // 创建动态代理处理类
        StartHandle startHandle = new StartHandle(realStart);
        Start proxy = (Start) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Start.class}, startHandle);
        // 处理业务
        proxy.sing();
    }
}

执行结果:

在这里插入图片描述

通过接口实现代理

UML

在这里插入图片描述

1 定义一个抽象角色Start,接口中定义抽象sing方法

2 真实角色实现Star接口,实现sing方法

3 定义一个中转处理接口MethodHandler,接口中定义invoke方法(后续处理,后续代理类方法全部先调用该方法进行处理)

4 定义处理类(具体实现)StartMethodHandle,实现invoke方法,通过反射调用真实角色对应方法

5 代理对象StartProxy,实现Start接口,重写接口,所有接口调用处理类(具体实现)的invoke方法,并传入真实角色

注意:JDK 实现代理的逻辑与其类似

具体实现

Start.java

// 抽象角色
// * 明星:具备唱歌能力
public interface Start {
    void sing() throws Throwable;
}

RealStart.java

// 真实角色
// * 实现明星唱歌
public class RealStart implements Start {
    @Override
    public void sing() throws Throwable {
        System.out.println("周杰伦唱歌");
    }
}

MethodHandler.java

// 中转处理接口
public interface MethodHandler {
    Object invoke(Object self, String methodName, Object[] args) throws Throwable;
}

StartMethodHandle.java

import java.lang.reflect.Method;
// 处理类(具体实现)
public class StartMethodHandle implements MethodHandler {
    @Override
    public Object invoke(Object self, String methodName, Object[] args) throws Throwable {
        System.out.println("代理执行之前");
        Method method = self.getClass().getMethod(methodName);
        Object invoke = method.invoke(self, args);
        System.out.println("代理执行之后");
        return invoke;
    }
}

StartProxy.java

// 代理对象
public class StartProxy implements Start {
    private MethodHandler methodHandler;
    private Start start;
    public StartProxy(MethodHandler methodHandler, Start start) {
        this.methodHandler = methodHandler;
        this.start = start;
    }
    @Override
    public void sing() throws Throwable {
        methodHandler.invoke(start,"sing",null);
    }
}

TestClient3.java

public class TestClient3 {
    public static void main(String[] args) {
        // 创建真实角色
        Start start = new RealStart();
        // 创建代理处理类
        StartMethodHandle startMethodHandle = new StartMethodHandle();
        // 创建代理对象
        StartProxy startProxy = new StartProxy(startMethodHandle, start);
        try {
            startProxy.sing();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

执行结果:

在这里插入图片描述

javassist字节码实现动态代理

Javassist 是一个开源的分析、编辑和创建 Java 字节码的库。它主要用于操作 Java 字节码,但也可以用来创建动态代理。

需要引入依赖如下

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.27.0-GA</version>
</dependency>

实现代码

Start.java

// 抽象角色
// * 明星:具备唱歌能力
public interface Start {
    void sing();
}

RealStart.java

/**
 * 真实角色
 * 实现明星唱歌
 *
 * @author Anna.
 * @date 2024/4/9 10:10
 */
public class RealStart implements Start {
    @Override
    public void sing() {
        System.out.println("周杰伦唱歌");
    }
}

MethodHandler.java

// 中转处理接口
public interface MethodHandler {
    Object invoke(Object self, String methodName, Object[] args) throws Exception;
}

StartMethodHandle.java

// 处理类
public class StartMethodHandle implements MethodHandler {
    @Override
    public Object invoke(Object self, String methodName, Object[] args) throws Exception {
        System.out.println("代理执行之前");
        Method method = self.getClass().getMethod(methodName);
        Object invoke = method.invoke(self, args);
        System.out.println("代理执行之后");
        return invoke;
    }
}

JavassistProxyFactory.java

import javassist.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class JavassistProxyFactory {

    /**
     * 创建代理类:相当于实现demo3中StartProxy
     *
     * @param target   真实角色
     * @param methodHandler  处理类(具体实现)
     * @return T
     * @author Anna.
     * @date 2024/4/9 17:26
     */
    public static <T> T createProxy(T target, MethodHandler methodHandler) throws Exception {
        // 创建 ClassPool,它是 Javassist 的核心类,用于处理类
        ClassPool pool = ClassPool.getDefault();
        // 获取目标类的 CtClass 对象
        CtClass ctClass = pool.get(target.getClass().getName());
        // 获取目标类的 CtClass 对象
        CtClass methodHandlerCtClass = pool.get(methodHandler.getClass().getName());
        // 创建代理类的名称
        String proxyClassName = target.getClass().getName() + "$Proxy";
        // 创建代理类的 CtClass 对象
        CtClass proxyCtClass = pool.makeClass(proxyClassName);
        // 设置代理类继承自目标类 用于实现其所有方法
        proxyCtClass.setSuperclass(ctClass);

        // 添加字段
        CtField aField = new CtField(ctClass, "a", proxyCtClass);
        aField.setModifiers(javassist.Modifier.PRIVATE);
        CtField bField = new CtField(methodHandlerCtClass, "b", proxyCtClass);
        bField.setModifiers(javassist.Modifier.PRIVATE);

        // 添加属性
        proxyCtClass.addField(aField);
        proxyCtClass.addField(bField);

        // 创建一个构造函数来初始化name属性
        CtConstructor constructor = new CtConstructor(new CtClass[]{ctClass,methodHandlerCtClass}, proxyCtClass);
        constructor.setModifiers(javassist.Modifier.PUBLIC);
        // $0 this $1 第一个参数 $2 第二个参数 依次类推
        constructor.setBody("{this.a = $1;this.b = $2;}");
        proxyCtClass.addConstructor(constructor);

        // 遍历目标类的所有方法,并创建相应的方法在代理类中
        for (Method method : target.getClass().getDeclaredMethods()) {
            // 创建方法
            CtMethod ctMethod = new CtMethod(pool.get(method.getReturnType().getName()), method.getName(), getParameterTypes(method), proxyCtClass);
            ctMethod.setModifiers(Modifier.PUBLIC);
            // 设置方法体,调用 MethodHandler 的 invoke 方法
            ctMethod.setBody("{ b.invoke(a, \"" + method.getName() + "\", $args); }");
            // 添加方法到代理类
            proxyCtClass.addMethod(ctMethod);
        }

        // 反射
        Class<?>  aClass = proxyCtClass.toClass();
        Constructor<?>[] constructors = aClass.getConstructors();
        Object obj = constructors[0].newInstance(target, methodHandler);
        // 创建代理类的实例
        return (T) obj;
    }

    /**
     * 获取方法参数列表
     *
     * @param method
     * @return javassist.CtClass[]
     * @author Anna.
     * @date 2024/4/9 17:40
     */
    private static CtClass[] getParameterTypes(Method method) throws NotFoundException {
        CtClass[] ctClasses = new CtClass[method.getParameterTypes().length];
        for (int i = 0; i < method.getParameterTypes().length; i++) {
            ctClasses[i] = ClassPool.getDefault().get(method.getParameterTypes()[i].getName());
        }
        return ctClasses;
    }

}

TestClient4.java

public class TestClient4 {
    public static void main(String[] args) throws Exception {
        Start realStart = new RealStart();
        StartMethodHandle startMethodHandle = new StartMethodHandle();
        Start proxy = (Start) JavassistProxyFactory.createProxy(realStart, startMethodHandle);
        proxy.sing();
    }
}

执行结果:

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

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

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

相关文章

蓝桥杯简单STL

目录 vector vector定义 vector访问 常用函数 size() ​编辑 push_back(num) pop_back() clear 迭代器&#xff08;iterator) 迭代器定义 遍历数组示例 insert(it, element) erase(it) 标准模板库--STL&#xff0c;它包含了多种预定义的容器、算法和迭代器&…

算法——倍增

. - 力扣&#xff08;LeetCode&#xff09; 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#xff0c;其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。 树节点的第 k 个祖先节点是从该节点到根节点路径…

SQL注入sqli_libs靶场第一题

第一题 联合查询 1&#xff09;思路&#xff1a; 有回显值 1.判断有无注入点 2.猜解列名数量 3.判断回显点 4.利用注入点进行信息收集 爆用户权限&#xff0c;爆库&#xff0c;爆版本号 爆表&#xff0c;爆列&#xff0c;爆账号密码 2&#xff09;解题过程&#xff1…

【前端捉鬼记】使用nvm切换node版本后再用node -v查看仍然是原来的版本

今天遇到一个诡异的问题&#xff0c;使用nvm切换node版本&#xff0c;明明提示已经切换成功&#xff0c;可是再次查看node版本还是之前的&#xff01; 尝试了很多办法&#xff0c;比如重新打开一个cmd窗口、切换前执行nvm install version都没成功&#xff0c;直到找到这篇文章…

# ABAP SQL 字符串处理

经常我都要在ABAP的sql语句中对字符串进行处理&#xff0c;现在就总结一下可以用到的方法 文章目录 字符串处理拼接字段运行结果 填充字符串运行结果 截取字符串 SUBSTRING运行结果 CAST转换类型程序运行结果 字符串处理 在SQL语句中&#xff0c;有时候会有需要拼接字段或者是…

Java 集合Collection

集合的体系 Collection的结构体系 List系列集合&#xff1a;添加的元素是有序的、可重复、有索引。Set系列集合&#xff1a;无序、不重复、无索引 HashSet&#xff1a;无序、不重复、无索引LinkedHashSet:有序、不重复、无索引TreeSet&#xff1a;按照大小默认升序排序、不重复…

ES6基础(JavaScript基础)

本文用于检验学习效果&#xff0c;忘记知识就去文末的链接复习 1. ECMAScript介绍 ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;英文名称是European Computer Manufacturers Association&#xff09;通过ECMA-262标准化的脚本程序设计语言…

SRNIC、选择性重传、伸缩性、连接扩展性、RoCEv2优化(六)

参考论文SRDMA&#xff08;A Scalable Architecture for RDMA NICs &#xff09;&#xff1a;https://download.csdn.net/download/zz2633105/89101822 借此&#xff0c;对论文内容总结、加以思考和额外猜想&#xff0c;如有侵权&#xff0c;请联系删除。 如有描述不当之处&…

Python中大的一把锁

今天可以来讲解下GIL是个什么了。 GIL为什么是Python中大的一把锁&#xff1f; GIL是Global Interpreter Lock的缩写&#xff0c;翻译过来就是全局解释器锁。 从字面上去理解&#xff0c;它就是锁在解释器头上的一把锁&#xff0c;它使Python代码运行变得有序。 假如有一段…

vue2 使用vue-org-tree demo

1.安装 npm i vue2-org-tree npm install -D less-loader less安装 less-loader出错解决办法&#xff0c;直接在package.json》devDependencies下面加入less和less-loader版本&#xff0c;然后执行npm i &#xff0c;我用的nodejs版本是 16.18.0&#xff0c;“webpack”: “^4…

上位机图像处理和嵌入式模块部署(qmacvisual缺失的颜色检测)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在图像处理当中&#xff0c;对颜色的处理是非常重要的。当然&#xff0c;这要建立在拍摄图片是彩色图片的基础之上。工业上面&#xff0c;虽然是黑…

wsl初步使用记录

wsl介绍 WSL是windows平台下Linux环境的子系统&#xff08;Windows Subsyetem for Linux&#xff09;&#xff0c;可以让Windows下方便的安装Linux系统&#xff0c;而无需安装其他虚拟机软件。 wsl使用 Windows操作系统支持 Windows 10 版本 2004 及更高版本&#xff08;内…

ruoyi-nbcio-plus基于vue3的flowable的自定义业务单表例子的升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

Vant DropdownMenu 下拉菜单带搜索功能

Vant DropdownMenu 下拉菜单带搜索功能 效果图&#xff1a; 上代码&#xff1a; <van-dropdown-menu active-color"#E33737"><van-dropdown-item ref"dropdownItem"><template #title><span>{{ dropdownItem.text }}</span…

【数据结构】:顺序表专题

前言&#xff1a;今天我们开始介绍数据结构有关内容&#xff0c;那么数据结构是什么呢&#xff1f; 数据结构是计算机存储、组织数据的方式。在工作中&#xff0c;我们通常会直接使用已经封装好的集合API(应用程序编程接口)&#xff0c;这样可以更高效地完成任务。但是作为一名…

三角形最小路径和-java

题目描述: 给定一个三角形 triangle &#xff0c;找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 1 的两个结点。也就是说&#xff0c;如果正位于当前行的下标 i &#xff…

JavaScript教程:从基础到发展历程及语法规则的全面介绍

文章目录 一、JavaScript简介二、JavaScript发展历程三、JavaScript基础语法3.1、变量概念3.2、变量命名3.3、变量提升3.4、代码注释3.5、语句3.6、区块 四、总结 一、JavaScript简介 JavaScript 是一种高级的、解释型的编程语言&#xff0c;主要用于为网页添加交互性和动态效…

网络安全(防火墙,IDS,IPS概述)

问题一:什么是防火墙,IDS,IPS? 防火墙是对IP:port的访问进行限制,对访问端口进行制定的策略去允许开放的访问,将不放开的端口进行拒绝访问,从而达到充当防DDOS的设备。主要是拒绝网络流量,阻断所有不希望出现的流程,禁止数据流量流通,达到安全防护的作用。如将一些恶…

谷歌浏览器快捷键, VScode 快捷键

谷歌浏览器快捷键 谷歌浏览器跳转标签页的方式&#xff1a; control Tab 跳转下一个标签页 control shift tab 上一个标签页 command 1-8 跳转对应的标签页&#xff0c;而command 9 则是跳转最后一个标签页 Previous Tab 插件实现谷歌浏览器两个tab页来回切换。快捷键为…

JVM-结合MAT工具分析OOM问题

JVM-结合MAT工具分析OOM问题 启动项目前配置VM参数 -XX:UseParNewGC -XX:UseConcMarkSweepGC -Xms1m #初始化大小 -Xmx1m #最大值 -XX:PrintGCDetails -Xloggc:gc_dandan.log -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./运行结果截图 使用MAT打开java_pid12164.hprof…