设计模式——代理模式

2023-10-27

代理模式介绍

其实在我们生活中各行各业也都会设计到代理,比如租房子,我们可以找中介公司,让中介公司帮我们去找合适的房源,这就是代理模式,再比如,有些同学找男朋友女朋友也会通过中间人去介绍,这也是一种代理模式。在我们编程中,由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这样代理对象就能给目标对象提供保护。

代理模式特点

优点:
① 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
② 代理对象可以扩展目标对象的功能
③ 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点:
① 增加系统复杂度
② 降低处理请求的速度

代理模式的结构

主角:
① 抽象主题类:就是需要提供一个接口或者抽象类给被代理对象去实现或者继承,里面提供被代理对象需要实现的一系列方法。

package proxy;

/**
 * 抽象主题类
 */
public interface Subject {
    void request();
}

② 真实主题类:需要被代理的对象,实现了抽象主题类中的具体业务方法。

package proxy;

/**
 * 真实主题
 */
public class SubjectImpl implements Subject{
    @Override
    public void request() {
        System.out.println("真实主题处理请求");
    }
}

③代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

package proxy;

/**
 * 代理类
 */
public class ProxySubject implements Subject{
    private Subject subject = new SubjectImpl();
    @Override
    public void request() {
        this.preRequest();
        subject.request();
        this.postRequest();
    }

    private void preRequest()
    {
        System.out.println("访问真实主题之前的功能扩展。");
    }
    private void postRequest()
    {
        System.out.println("访问真实主题之后的功能扩展。");
    }
}

测试:

package proxy;

public class start {
    public static void main(String[] args) {
        ProxySubject proxySubject = new ProxySubject();
        proxySubject.request();
    }
}

测试结果:

访问真实主题之前的预处理。
真实主题处理请求
访问真实主题之后的后续处理。

代理模式结构图:
结构图

动态代理

上面的代理模式比较死板,假如我们要增加被代理对象,那么也要增加响应的代理对象,有没有一种方式能否自动设置被代理对象以及需要被代理的方法呢?有,这种方式就是动态代理,动态代理典型的例子那就是SpringAOP。
在JDK中,Proxy类提供了一个静态方法———newProxyInstance,内容如下:

public static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces,
   InvocationHandler invocationHandler) throws IllegalArgumentException

给予我们来生成一个代理对象(proxy),它有3个参数:
classLoader——类加载器;
interfaces——绑定的接口,也就是把代理对象绑定到哪些接口下,可以是多个;
invocationHandler ——绑定代理对象逻辑实现。
我们再来看看invocationHandle接口,他定义了一个invoke方法,这个方法就是实现代理对象的逻辑。

/**
* 处理代理对象方法逻辑
* @param proxy 代理对象
* @param method 当前方法
* @param args 运行参数
* @return 方法调用结果
*/
public Object invoke(Object proxy, Method method, Object[] args);

然后通过目标对象(target)、方法(method)和参数(args)就能够反射方法运行了。
很多项目中我们都有用到拦截器,我们就用动态代理实现一个拦截器的功能。

首先我们来定义一个需要被代理的对象以及它的接口。

package com.springboot.chapter4.service;
public interface HelloService {
   public void sayHello(String name);
}
package com.springboot.chapter4.service.impl;
import com.springboot.chapter4.service.HelloService;
public class HelloServiceImpl implements HelloService {
   @Override
   public void sayHello(String name) {
     if (name == null || name.trim() == "") {
       throw new RuntimeException ("parameter is null!!");
     }
     System.out.println("hello " + name);
   }
}

我们准备在sayHello前后通过拦截器扩展一些功能,扩展功能也是代理模式的特点之一,定义一个拦截器接口:

package com.springboot.chapter4.intercept;

import com.springboot.chapter4.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

public interface Interceptor {
    /**
     * 事前方法
     */
    boolean before();

    /**
     * 事后方法
     */
    void after();

    /**
     * 反射方法
     * @param invocation {@link Invocation}
     * @return
     */
    Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;

    /**
     * 是否返回方法,事件没有发生异常
     */
    void afterReturning();

    /**
     * 事件发生异常后执行
     */
    void afterThrowing();

    /**
     * 是否使用了around方法替代原方法
     */
    boolean useAround();
}

然后定义一个具体的拦截器,实现拦截器接口

package com.springboot.chapter4.intercept;

import com.springboot.chapter4.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

public class MyInterceptor implements Interceptor {

    /**
     * 事前方法
     */
    @Override
    public boolean before() {
        System.out.println("before ......");
        return true;
    }

    /**
     * 事后方法
     */
    @Override
    public void after() {
        System.out.println("after ......");
    }

    /**
     * 反射方法
     *
     * @param invocation {@link Invocation}
     * @return
     */
    @Override
    public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        System.out.println("around before ......");
        Object proceed = invocation.proceed();
        System.out.println("around after ......");
        return proceed;
    }

    /**
     * 是否返回方法,事件没有发生异常
     */
    @Override
    public void afterReturning() {
        System.out.println("afterReturning ......");
    }

    /**
     * 事件发生异常后执行
     */
    @Override
    public void afterThrowing() {
        System.out.println("afterThrowing ......");
    }

    /**
     * 是否使用了around方法替代原方法
     */
    @Override
    public boolean useAround() {
        return true;
    }
}

invocation源码:

package com.springboot.chapter4.invoke;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Invocation {
   private Object[] params;
   private Method method;
   private Object target;
   public Invocation(Object target, Method method, Object[] params) {
     this.target = target;
     this.method = method;
     this.params = params;
   }
   // 反射方法
   public Object proceed() throws
     InvocationTargetException, IllegalAccessException {
     return method.invoke(target, params);
   }
   /**** setter and getter ****/
}

注意invovation中的proceed方法,他会以反射的方式去调用原有的方法。
我们提供一个ProxyBean,它需要能够获取代理对象,以及处理代理对象方法逻辑。获取代理对象我们前面说到在JDK中,Proxy类提供了一个静态方法———newProxyInstance,处理代理对象方法逻辑我们可以实现invocationHandle接口。

package com.springboot.chapter4.invoke;

import com.springboot.chapter4.intercept.Interceptor;
import com.springboot.chapter4.intercept.MyInterceptor;
import com.springboot.chapter4.service.HelloService;
import com.springboot.chapter4.service.impl.HelloServiceImpl;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyBean implements  InvocationHandler{
    private Object target = null;
    private Interceptor interceptor = null;

    /**
     * 处理代理对象方法逻辑
     *
     * @param proxy  代理对象
     * @param method 当前方法
     * @param args   运行参数
     * @return 方法调用结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        //异常标识
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, args);
        Object retObject = null;
        try {
            if(this.interceptor.before()){
                retObject = this.interceptor.around(invocation);
            }else {
                retObject = method.invoke(target, args);
            }
        }catch (Exception e){
            exceptionFlag = true;
        }
        this.interceptor.after();
        if(exceptionFlag){
            this.interceptor.afterThrowing();
        }else{
            this.interceptor.afterReturning();
            return retObject;
        }
        return null;
    }

    /**
     * 绑定代理对象
     * @param target 被代理的对象
     * @param interceptor 拦截器
     * @return 代理对象
     */
    public static Object getProxyBean(Object target, Interceptor interceptor){
        ProxyBean proxyBean = new ProxyBean();
        //保存被代理对象
        proxyBean.target = target;
        //保存拦截器
        proxyBean.interceptor = interceptor;
        //生成代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyBean);
    }

    public static  void testProxy(){
        HelloService helloService = new HelloServiceImpl();
        HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
        proxy.sayHello("ZhangSan");

        System.out.println("########################name is null!!");
        proxy.sayHello(null);
    }
}

测试类:

package com.springboot.chapter4.invoke;

import org.junit.jupiter.api.Test;

class ProxyBeanTest {

    @Test
    void testProxy() {
        ProxyBean.testProxy();
    }
}

运行结果:

before ......
around before ......
hello ZhangSan
around after ......
after ......
afterReturning ......
########################name is null!!
before ......
around before ......
after ......
afterThrowing ......

到这里我们成功用动态代理扩展了sayHello的功能。编程是门实践学科,通过自己动手会有更加深入的理解哦!
想了解更多设计模式的可以关注我,写的不好的地方请多多指教,共同进步!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式——代理模式 的相关文章

  • 从txt文件中读取数据而不下载它?

    我想从提供的文本文件中解析信息 有没有一种方法可以在应用程序中执行此操作 而无需先下载文件 以某种方式传输文本内容 打开到 URL 的 Http 连接 使用内置 HttpURLConnection 或使用 commons httpclien
  • 在Java Servlet中获取通过jquery ajax发送的参数[重复]

    这个问题在这里已经有答案了 我在网上搜索这个主题 但找不到有效的示例 我会很高兴有人能给我帮助 这就是我测试的 ajax url GetJson type POST dataType json contentType application
  • 使用itext java库复制时pdf文件大小大大增加

    我正在尝试使用 Java 中的 itextpdf 库将现有的 pdf 文件复制到一些新文件中 我使用的是 itextpdf 5 5 10 版本 我在两种方式上都面临着不同的问题 PDFStamper 和 PdfCopy 当我使用 PDFSt
  • 来自行号的方法名称

    给定特定类源代码 Java C 的行号 是否有一种简单的方法来获取它所属的方法的名称 如果它落入其中 大概使用抽象语法树 这对于将 checkstyle 的输出限制为仅触及的方法很有用 我假设您必须使用抽象语法树来执行 Line gt Me
  • 从 Android 函数更新 Textview

    有人可以告诉我如何从函数更新 Android Textview 控件吗 我在互联网上进行了深入搜索 看到很多人都问同样的问题 我测试了线程但无法工作 有人有一个简单的工作示例吗 例如 调用一个函数 在循环中运行多次 并且该函数在 TextV
  • 在java代码中创建postgresql表

    我有一个与 postgreSQL 数据库连接的 java 代码 现在 我希望当它连接到数据库时 我还将创建数据库表 但我的问题是 它不会创建数据库 我不知道问题是什么 这是我的代码 Statement st null ResultSet r
  • JAX-WS 入门 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有人可以推荐一些关于 JAX WS 入门的好教程吗 使用各种工具 如 wsgen 等 您可以从这里开始 通过 Java SE 6 平台介绍
  • 如何杀死 Java Future?

    我正在开发的服务使用 Future 来并行运行多个任务 每个任务最多可能需要一分钟才能完成 然而 外部库似乎有问题 因为在某些情况下 2 的时间 它不会返回 在这些情况下 我想给出 2 分钟的等待时间 如果还没有返回 我想杀死 future
  • Google 表格使用 API 密钥而不是 client_secret.json

    In the QuickStart java示例Java 快速入门 https developers google com sheets api quickstart java他们使用OAuth client ID识别该应用程序 这会弹出一
  • 是否可以创建 Java RAM 磁盘以与 java.io.* API 一起使用?

    我正在使用一个第三方库 它基本上创建一个输出目录 其中包含不同类型的文件和子目录 我希望能够编写单元测试来确认输出正确 我希望能够将库与 RAM 磁盘一起使用 这样库所做的任何事情都不会以任何方式接触实际的磁盘板 这个想法是让测试运行和清理
  • java.exe 以非零退出值 1 结束

    只是为了开始 我并不是真正尝试从 Android 中的 xlsx 文件中读取单元格 我已经尝试了几乎所有我在 Google 上搜索到的内容 但是每次 在两台不同的 PC 上 都是 Java 1 7 0 79 当我尝试构建 运行 这个应用程序
  • 如何获取 JDBC 中 UPDATE 查询影响的所有行?

    我有一项任务需要使用更新记录PreparedStatement 一旦记录被更新 我们知道更新查询返回计数 即受影响的行数 但是 我想要的不是计数 而是受更新查询影响的行作为响应 或者至少是受影响的行的 id 值列表 这是我的更新查询 UPD
  • 如何在 JmsMessagingTemplate.sendAndReceive 上设置等待超时

    我在 MVC 控制器中使用 JmsMessagingTemplate 的 sendAndReceive 但如果没有发送回复消息 它似乎会永远等待回复 该文档指出 返回 回复 如果无法接收消息 例如由于超时 则可能为 null 然而 我只是不
  • React Native v0.71.8 React-native-vector-icons 你看不到的图标

    我在用react native版本v0 71 8 我安装了react native vector icons库 但图标未显示 似乎链接在最新版本的 React Native 中不再起作用 所以我按照说明进行操作 但它不再编译 出现以下错误
  • 添加 char 和 int

    据我了解 字符是一个字符 即一个字母 一个digit 标点符号 制表符 空格或类似的东西 因此 当我这样做时 char c 1 System out println c 输出 1 正是我所期望的 那么为什么当我这样做时 int a 1 ch
  • 在 Tensorflow-lite Android 中将位图转换为 ByteBuffer(浮点)

    在用于图像分类的tensorflow lite android演示代码中 图像首先转换为ByteBuffer格式以获得更好的性能 这种从位图到浮点格式的转换以及随后到字节缓冲区的转换似乎是一个昂贵的操作 循环 按位运算符 float mem
  • 如何更改 JAX-WS Web 服务的地址位置

    我们目前已经公开了具有以下 URL 的 JAX RPC Web 服务 http xx xx xx xx myservice MYGatewaySoapHttpPort wsdl http xx xx xx xx myservice MYGa
  • 如何在不使用 -cp 开关的情况下在 Groovy 中自动加载数据库 jar?

    我想简化调用 Oracle 数据库的 Groovy 脚本的执行 如何将 ojdbc jar 添加到默认类路径以便我可以运行 groovy RunScript groovy 代替 groovy cp ojdbc5 jar RunScript
  • Spring MVC:通用 DAO 和服务类

    我正在 Spring MVC 中编写网页 我使用 Generic DAO 编写了所有 DAO 现在我想重写我的服务类 我该如何写 通用服务 我的 DAO 如下 DAO package net example com dao import j
  • MyBatis 枚举的使用

    我知道以前有人问过这个问题 但我无法根据迄今为止找到的信息实施解决方案 所以也许有人可以向我解释一下 我有一个表 状态 它有两列 id 和 name id是PK 我不想使用 POJO Status 而是使用枚举 我创建了这样一个枚举 如下所

随机推荐

  • Arduino实现真正的物联网-通过MQTT服务器-两块板子远程通信

    该博客现已搬运到我的个人博客 上 之后我也会在自己的博客上更新一些东西 这里放上文章链接 我在做竞赛项目的时候 在板子和板子远程交互上做的东西 百度上找到的东西也都是七零八碎的 同时也希望我的博客能给大家一点微薄的帮助 PS 我也是第一次写
  • 爬虫逆向实战(30)-某查查股东关联公司(HmacSHA512)

    一 数据接口分析 主页地址 某查查 1 抓包 通过抓包可以发现数据接口是api people getRelatCompany 2 判断是否有加密参数 请求参数是否加密 无 请求头是否加密 通过查看 标头 可以发现 请求头中有一个key和va
  • React入门(二)

    React入门 二 这一讲 我们来说说React的代码细节 一 整体结构 React官网给出的这个案例 实现的功能是一个小游戏 tic tac toe 游戏在一个九宫格中进行 双方对弈 一方使用圈 另一方使用叉 看看谁先在横 竖或对角线上形
  • 【前端项目】博客系统(页面设计)

    文章目录 一 预期效果 二 实现博客列表页 三 实现博客正文页 四 实现博客登录页 五 实现博客编辑页 一 预期效果 代码详情见 gitee链接 博客列表页效果 博客详情页效果 博客登录页效果 博客编辑页效果 二 实现博客列表页 blog
  • C++中to_string函数的用法

    to string 函数 将数字常量转换为字符串 返回值为转换完毕的字符串 头文件 include lt string gt 必须加 string s to string i 将整数i转换为字符串表示形式 string to string
  • WebRtc集锦

    1 RTP协议全解析 H264码流和PS流 对牛乱弹琴的博客 CSDN博客 rtp流和ps流 WebRTC研究 2 Kurento作为IPC的WebRTC网关 译 对牛乱弹琴的博客 CSDN博客 音视频传输 RTP协议详解和H 264打包方
  • 动态规划(C语言实现)

    文章目录 动态规划的介绍 动态规划的定义 什么叫做分治递归 递推 递归 1 定义 2 引入 分而治之 1 定义 2 过程 什么叫做记忆储存 斐波拉契数列的递归求法 斐波拉契数的定义 传统的递归解法 动态规划解法 感悟与思考 动态规划 动态规
  • 高并发模拟多个用户同时访问接口

    前些天客户需要测试一下接口可以承受多大的并发量 当时就想到了countDownLatch 就小小的提了一下意见就被安排到我的头上 废话就不多说了 为了不浪费你们的时间 先说好这代码是测试单个接口或者方法的并发量的 先说思路 再上代码 整体代
  • 通过id获取input标签元素对象_学点新姿势: 通过DOM Clobbering 引发GMail的的XSS

    什么是AMP4Email AMP4Email 也称为动态邮件 是Gmail的一项新功能 可使电子邮件包含动态HTML内容 尽管编写包含HTML标签的电子邮件已经存在了很多年 但通常情况下都是假定HTML只包含静态内容 即某种格式 和一些图像
  • 计算机网络——快速了解常见应用层协议体系

    文章目录 加密算法 对称加密 非对称加密 一 远程登录 TELNET SSH 1 Telnet 2 SSH 二 文件传输 FTP SFTP FTPS 1 FTP 2 SFTP 3 FTPS SSL工作原理 三 电子邮件 SMTP POP I
  • Gdal-master、PROJ 8.2.1源码下载及编译(Win10+VS2022)

    为了编译Gdal master的源码 需要PROJ的支持 而PROJ又需要SQLite的支持 在电脑创建目录 D My3rdParty 其下设置include lib bin子目录 用于保存源码编译结果 并将目录 D My3rdParty
  • 离散数学(七):一阶逻辑符号化

    离散数学 六 习题课 见雨课堂随堂测试1 2 3 之前学习了命题逻辑 那一阶逻辑和命题逻辑有什么区别呢 其实一阶逻辑是命题逻辑的细化 它将命题分成了个体词和谓词 至于为什么这么做 我的理解是描述的更加细化了 加上量词的辅助作用 可以表达存在
  • 【python】给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

    示例 1 输入 123 输出 321 示例 2 输入 123 输出 321 示例 3 输入 120 输出 21 注意 假设我们的环境只能存储得下 32 位的有符号整数 则其数值范围为 231 231 1 请根据这个假设 如果反转后整数溢出那
  • html元素的title属性

    Title 属性 The title global attribute contains text representing advisory information related to the element it belongs to
  • (六十五)基于决策树的信用评级

    决策树的结构与划分选择 20世纪70年代末至20世纪80年代初 Quinlan开发了一系列决策树模型 最开始是ID3算法 后来是C4 5算法 随后又发布了C5 0算法 1984年 多位统计学家在著名的 Classification and
  • QPixmap vs. QImage

    Development Tutorials Graphics Performance lt Development Tutorials Contents 1 QPixmap vs QImage 1 1 QImage 1 2 QPixmap
  • docker入门实践,实战搭建nginx

    查找现成的nginx镜像 可以通过docker search nginx 命令来获取 可以看到有很多 我们一般选取stars最多 即第1个来使用 docker search nginx NAME DESCRIPTION STARS OFFI
  • 牛客网-数列还原

    数列还原 解题思路 稍加分析 根据数据规模 先对模糊的数字全排列吗 然后组成完整的数组 再判断完整的数组是否满足条件 失误 把简单的问题想的有点复杂了 import java util public class Main public st
  • 从键盘输入一个字符,判断该字符是否大写字母、小写字母、数字字符或其他字符。分别输出对应的提示信息。

    输入要求 输入一个字符 输出要求 如果该字符是大写字母 则输出 upper 若是小写字母 则输出 lower 若是数字字符 则输出 digit 若是其他字符 则输出 other 输出不含双引号 输入样例 1 输出样例 digit inclu
  • 设计模式——代理模式

    代理模式 代理模式介绍 代理模式特点 代理模式的结构 动态代理 代理模式介绍 其实在我们生活中各行各业也都会设计到代理 比如租房子 我们可以找中介公司 让中介公司帮我们去找合适的房源 这就是代理模式 再比如 有些同学找男朋友女朋友也会通过中