【java设计模式】——代理设计模式,两种举例说明

news2024/11/18 17:42:34

代理设计模式

1.介绍

Spring 框架中AOP底层使用动态代理设计模式。通过学习动态代理设计模式可以很好的理解Spring框架AOP底层

代理模式(Proxy)是GoF23种设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

image-20211224090242901

代理设计模式包括:静态代理和动态代理。

静态代理:代理对象由程序员自己编写,里面提供硬编码方式来访问调用者

动态代理:

  • JDK动态代理:一种基于接口的代理实现
  • Cglib动态代理:一种基于继承的代理实现

代理设计模式优点:

  • 保护代理对象。客户不能直接访问真实对象。只能通过代理对象进行访问
  • 可扩展性。复合开闭原则,可以实现在不修改真实代理对象情况下,进行扩展。
  • 对于一些访问困难的真实对象,可以使用代理对象,更轻松的访问。

代理设计模式缺点:

  • 系统复杂性增加。类的数目增加,代码相对更复杂一点。
  • 性能降低。每次访问真实对象,只能通过代理对象。

2.静态代理示例

以房屋买卖为例子(简易流程),客户想要买房,找中介,由中介带看房东的房子。

客户看好了,想要买,房东也同意买了,中介收取客户的中介费。

daili

创建房东(真实对象)

新建com.spring.proxy.staticproxy.Fangdong

public class Fangdong {
  public void bigHouse(){
    System.out.println("房东的大房子");
   }
}

创建中介类(代理对象)

新建com.spring.proxy.staticproxy.Zhongjie

public class Zhongjie {
  public void zhongjie(){
    System.out.println("约谈客户,进行带看");// 扩展功能
    Fangdong fangdong = new Fangdong();
    fangdong.bigHouse();
    System.out.println("交易成功,收取中介费"); // 扩展功能
   }
}

创建客户类(调用者)

com.spring.proxy.staticproxy.Kehu

public class Kehu {
  public static void main(String[] args) {
    Zhongjie zhongjie = new Zhongjie();
    zhongjie.zhongjie();
   }
}

3.动态代理示例

3.1JDK动态代理

JDK动态代理是基于接口来实现的,底层是基于Java 反射技术实现的。

创建接口

创建接口com.spring.proxy.jdkproxy.House

因为JDK动态代理要求调用者必须实现接口。所以先建立接口

public interface bigHouse {
    void bighose();
}

创建真实对象类

创建类com.spring.proxy.jdkproxy.Fangdong

/**
 * JDK动态代理强制要求:真实的被调用的类必须实现接口
 */
public class FangDong implements bigHouse{
    @Override
    public void bighose() {
        System.out.println("大房子");
    }
}

创建代理类

创建com.spring.proxy.jdkproxy.Zhongjie

/**
 * 在JDK动态代理底层是基于java的反射机制实现的。本质上就是方法的调用
 *
 * 负责调用真实对象方法的类,必须实现InvocationHandler接口
 */
public class ZhongJie implements InvocationHandler {

    private FangDong fangDong;

    public ZhongJie(FangDong fangDong) {
        this.fangDong = fangDong;
    }

    /**
     *
     * @param proxy 产生的代理对象
     * @param method 真实对象中被调用的方法对象
     * @param args 被调用方法的参数
     * @return 被调用方法的返回值,都当做invoke方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("带看");

        //坑:千万不要用invoke方法的第一个参数proxy,他是代理对象,而不是真实对象
        Object result = method.invoke(fangDong, args);

        System.out.println("中介费");

        return result;
    }
}

创建客户类

创建com.spring.proxy.jdkproxy.Kehu

public class KeHu {

    public static void main(String[] args) {


        /**
         * 参数1:ClassLoader   在当前程序中,所有类的类加载器都是同一个对象
         *       作用:让生成的代理对象立即生效,(invoke中的proxy对象)
         * 参数2:Class[],放接口,告诉编译器,调用的是哪个方法
         *       作用:数组中放哪个接口,接口中的方法对象就会传递给invoke中的method方法对象
         * 参数3:InvocationHandler对象,负责调用真实对象的处理类,就是ZhongJie
         */
        //动态代理:动态:不是就调用某个对象的方法,而是想调哪个对象都可以
        InvocationHandler zhongJie = new ZhongJie(new FangDong());
        bigHouse bigHouse = (bigHouse) Proxy.newProxyInstance(Kehu.class.getClassLoader(), 
                                                              new Class[]{bigHouse.class}, 																					zhongJie);
        bigHouse.bighose();
    }
}

第二例子
// 被代理接口
public interface Apple {
  String sell(double price);//卖产品
  void repair();// 维修
}


// 被代理接口的实现类
public class AppleImpl implements Apple{
  @Override
  public String sell(double price) {
    System.out.println("产品卖了"+price+"元");
    return "iphone13";
   }


  @Override
  public void repair() {
    System.out.println("苹果售后维修");
   }
}


// 代理方式类,定义被代理方法的增强方式
// 该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式
public class ShoppingProxy implements InvocationHandler {
  private Apple apple;// 被代理对象
  public ShoppingProxy(Apple apple) {
    this.apple = apple;
   }


  /**
   * 定义原方法的增强方式
   * @param proxy 被代理对象
   * @param method 被代理对象调用的方法
   * @param args 被代理对象调用的方法时,传入的参数
   * @return 方法的返回值
   * @throws Throwable
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String name = method.getName(); //被代理对象执行的方法名
    if("sell".equals(name)){
      double price = (double)args[0]*0.9; //增强参数
      Object result = method.invoke(apple, price); // 执行方法
      return result + "和充电头"; // 增强返回值
     }else if("repair".equals(name)){
      System.out.println("专属客服为您服务!"); // 增强方法流程
      return method.invoke(apple,args);
     }else{
      return method.invoke(apple,args); // 什么都不增强
     }
   }
}


public class Test {
  public static void main(String[] args) {
    // 被代理对象
    Apple apple = new AppleImpl();
    // 代理方式对象
    ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
    // 生成代理对象
    Apple appleJD = (Apple) Proxy.newProxyInstance(
        apple.getClass().getClassLoader(), // 类加载器
        apple.getClass().getInterfaces(),//被代理接口
        shoppingProxy //代理方式对象
     );
    // 执行增强后的方法
    String sell = appleJD.sell(6000);
    System.out.println(sell);


    appleJD.repair();
   }
}

3.2Cglib动态代理

Cglig是基于继承的,是第三方提供的技术,需要导入jar包,并且。

    <dependencies>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
    </dependency>
  </dependencies>

创建房东类

/**
 * 真实被调用对象
 */
public class FangDong {
    public void bigHouse(){
        System.out.println("大房子");
    }
}

创建中介类

/**
 * 作用:调用真实对象的方法
 */
public class ZhongJie implements MethodInterceptor {


    /**
     *
     * @param o 目标对象:即FangDong对象
     * @param method 目标方法
     * @param objects 目标方法参数
     * @param methodProxy 生成的代理对象(即FangDong的子类)
     * @return 目标方法返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("带看");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("中介费");
        return  result;

    }
}

创建客户类

public class KeHu {

    public static void main(String[] args) {
        //所有方法都在Enhancer中
        Enhancer enhancer = new Enhancer();
        //1.setSuperClass(真实是哪个类)  真实是哪个类,根据这个类创建代理对象,即FangDong的子类
        enhancer.setSuperclass(FangDong.class);
        //2.setCallBack(对象)    设置由哪个类作为增强实现类
        enhancer.setCallback(new ZhongJie());
        //3.create()   创建代理对象,返回值为object类型,本质为代理对象
        FangDong fangDong = (FangDong) enhancer.create();
        fangDong.bigHouse();
    }

}

测试结果,发现出现异常,这是因为Java 17版本中的Java Platform Module System(java 9就开始有了)引起的,特别是强封装的实现。

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1376c05c

把代码原封不动的放在Java 11的环境中就可以使用。

如果必须要在Java17中使用添加JVM参数,表示允许使用未命名模块。

--add-opens java.base/java.lang=ALL-UNNAMED
第二例子
<!-- 引入cglib依赖 -->
<dependencies>
  <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
  </dependency>
</dependencies>
  
// 被代理类
public class Apple{
  public String sell(double price) {
    System.out.println("产品卖了"+price+"元");
    return "iphone13";
   }
  public void repair() {
    System.out.println("苹果售后维修");
   }
}


// 代理方式类,实现MethodInterceptor接口,重写intercept方法
public class ShoppingProxy implements MethodInterceptor {
  private Apple apple; // 被代理对象
  public ShoppingProxy(Apple apple) {
    this.apple = apple;
   }


  /**
   * 定义原方法的增强方式
   * @param o 被代理对象
   * @param method 被代理对象调用的方法
   * @param objects 被代理对象调用的方法时,传入的参数
   * @param methodProxy 底层生成的代理类的引用
   * @return 方法的返回值
   * @throws Throwable
   */
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    String name = method.getName();
    if("sell".equals(name)){
      double price = (double)objects[0]*0.8;
      Object result = method.invoke(apple, price);
      return result+"和数据线";
     }else if("repair".equals(name)){
      System.out.println("专属客服为您服务!");
      return method.invoke(apple,objects);
     }else{
      return method.invoke(apple,objects);
     }
   }
}


public class Test {
  public static void main(String[] args) {
    // 被代理对象
    Apple apple = new Apple();
    // 代理方式
    ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
    // 生成代理对象
    Apple appleTB = (Apple) Enhancer.create(Apple.class, shoppingProxy);


    // 执行增强后的方法
    String sell = appleTB.sell(9000);
    System.out.println(sell);
    appleTB.repair();
   }
}

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

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

相关文章

Web APIs—介绍、获取DOM对象、操作元素内容、综合案例—年会抽奖案例、操作元素属性、间歇函数、综合案例—轮播图定时器版

版本说明 当前版本号[20231204]。 版本修改说明20231204初版 目录 文章目录 版本说明目录复习变量声明 Web APIs - 第1天笔记介绍概念DOM 树DOM 节点document 获取DOM对象案例— 控制台依次输出3个li的DOM对象 操作元素内容综合案例——年会抽奖案例操作元素属性常用属性修改…

天池XGBoost,重写柱状图代码

天池XGBoost 地址 重写柱状图代码&#xff1a;我没考虑复杂度&#xff0c;只考虑直观理解 原文统计地点是否降雨来画柱状图实在是太麻烦了&#xff0c;我重写了一下。最麻烦的就是数据处理。我的思路是&#xff1a; 首先取下雨的全部数据data[data[RainTomorrow] Yes] 然后…

全网最牛最“刑”的Fiddler移动端抓包

本篇文章&#xff0c;博主想使用通俗易懂的话语&#xff0c;让大家明白以下内容&#xff1a; 什么是抓包哪些场景需要用到抓包Fiddler抓包的原理怎样使用Fiddler进行移动端抓包 抓包 包 (Packet) 是TCP/IP协议通信传输中的数据单位&#xff0c;一般也称“数据包”。 我们平常…

二维码智慧门牌管理系统升级:轻松解决重新制牌问题

文章目录 前言一、更便捷的申请方式二、系统优势 前言 随着科技的快速发展&#xff0c;智能化管理已经成为我们日常生活的一部分。最近&#xff0c;为了满足人们对门牌类型更换、门牌丢失等需要重新制牌的需求&#xff0c;二维码智慧门牌管理系统升级了解决方案&#xff0c;为…

Python如何从文件中读取数据

从文件中读取数据 1. 读取整个文件 要读取文件&#xff0c;首先来创建一个文件&#xff1a; 然后打开并读取这个文件&#xff0c;再将其内容显示到屏幕上&#xff1a; file_reader.py with open(pi_digits.txt) as file_object:contents file_object.read()print(contents)…

创新、升级丨数据手套FOHEART Pro开启手势识别新篇章!

在人机交互领域&#xff0c;我们始终追求更加自然、逼真的体验。正如现实生活中&#xff0c;我们习惯于通过语言和表情来传达思想和情感&#xff0c;然而&#xff0c;在虚拟世界中&#xff0c;人机交互需要以更加直观、生动的方式进行操作、控制和交互。 为了更好地满足市场的…

Elasticsearch:评估 RAG - 指标之旅

作者&#xff1a;Quentin Herreros&#xff0c;Thomas Veasey&#xff0c;Thanos Papaoikonomou 2020年&#xff0c;Meta发表了一篇题为 “知识密集型NLP任务的检索增强生成” 的论文。 本文介绍了一种通过利用外部数据库将语言模型 (LLM) 知识扩展到初始训练数据之外的方法。 …

如何使用Access中的窗体与数据打交道,看这篇文章就够了

Access数据库由一个或多个包含相关信息的表组成。一旦创建了这样一个空数据库,就可以遍历所有的表来填充信息。 然而,这可能会变得乏味和混乱。当你试图输入数据时,很难遵循表中数据之间的关系。访问输入窗体是一个很有价值的工具。你可以设计输入窗体,以便以逻辑格式输入…

python读取所有sheet内容到另一个文件中

实现效果&#xff1a; 将原excel中的步骤、预期效果列按回车拆成多行数据&#xff0c;其余字段值填充其他数据 实现结果&#xff1a; # This is a sample Python script.# Press ShiftF10 to execute it or replace it with your code. # Press Double Shift to search everyw…

单目相机测距(3米范围内)二维码实现方案(python代码 仅仅依赖opencv)

总体思路:先通过opencv 识别二维码的的四个像素角位置,然后把二维码的物理位置设置为 cv::Point3f(-HALF_LENGTH, -HALF_LENGTH, 0), //tl cv::Point3f(HALF_LENGTH, -HALF_LENGTH, 0), //tr cv::Point3f(HALF_LENGTH, HALF_LENGTH, 0), //br cv::P…

我有才满足于自媒体行业的知识付费平台课程

用户管理 提供会员特权和积分奖励&#xff0c;提高用户忠诚度和购买力。 用户通过在平台上进行消费、签到、参与活动等方式获取积分&#xff0c;用于兑换丰厚奖品或提升会员等级。增强用户的参与感与忠诚度&#xff0c;提高用户粘性&#xff0c;并刺激用户的购买力。 为用户打…

3.镜像加速器

目录 1 阿里云 2 网易云 从网络上拉取镜像的时候使用默认的源可能会慢&#xff0c;用国内的源会快一些 1 阿里云 访问 阿里云-计算&#xff0c;为了无法计算的价值 然后登录&#xff0c;登录后搜索 容器镜像服务 点击容器镜像服务 点击管理控制台 点击 镜像工具->镜像…

vue3 vue-cropper@next 实现图片裁切功能

Vue Cropper 实现上传图片预览&#xff0c;裁切上传效果 下载 pnpm add vue-croppernext使用 <template><inputref"inputRef"class"hidden"accept".png,.jpeg,.jpg"multipletype"file"change"handleUploadChange&quo…

基于ssm实现的工资管理系统

一、系统架构 前端&#xff1a;jsp | jquery | layui 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及截图 三、功能介绍 01. 登录页 02. 首页 03. 用户管理-基本资料 04. 用户管理-修改密码 05. 人事管理-部门列表…

Leetcode—409.最长回文串【简单】

2023每日刷题&#xff08;四十八&#xff09; Leetcode—409.最长回文串 强烈吐槽&#xff01;&#xff01;&#xff01; 非常不理解&#xff0c;同样的代码&#xff0c;为什么C跑不了C就跑得了&#xff0c;力扣编译器是对C语言有歧视吗&#xff1f;&#xff1f;&#xff1f;…

【springboot原理篇】Bean的加载方式,面试必看

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、上古时代原始方式&#xff1a; &#x1f4d5;XML文件 ~~bean定义 &#x1f440;演示获取bean&#xff1a; ❌缺点&#xff1a; &#x1f4d5;注解方式&#xff1a; ~~component ~~指定扫描路径&#…

如何使用 Oracle SQL Developer 连接 pgvector

如何使用 Oracle SQL Developer 连接 pgvector 1. 下载 postgresql 的 jdbc 驱动2. Oracle SQL Developer 配置第三方驱动3. Oracle SQL Developer 配置 postgres 连接 1. 下载 postgresql 的 jdbc 驱动 访问 https://jdbc.postgresql.org/download/&#xff0c;下载驱动&…

Linux部署HDFS集群前置准备

二、VMware准备Linux虚拟机 三、VMware虚拟机系统设置 &#xff08;一&#xff09;对三台虚拟机完成主机名、固定IP、SSH免密登陆等系统设置 1.配置固定IP地址 打开控制台&#xff0c;切换到root用户 su -修改主机名、IP地址 vim /etc/sysconfig/network-scripts/ifcfg-ens3…

Temu数据面板:Temu商家必备的数据分析工具

在Temu这个电商平台上&#xff0c;越来越多的商家意识到数据分析的重要性。数据分析可以帮助商家更好地了解店铺的运营情况&#xff0c;从而制定更有效的运营策略&#xff0c;提高销售业绩。而在这个过程中&#xff0c;Temu数据面板成为了一个不可或缺的工具。 先给大家推荐一款…

学习使用三个命令实现在腾讯云服务器TencentOS Server 3.1或者CentOS 8上安装ffmpeg

学习使用三个命令实现在腾讯云服务器TencentOS Server 3.1或者CentOS 8上安装ffmpeg Error: Unable to find a match: ffmpeg添加RPMfusion仓库安装SDL安装ffmpeg执行命令测试 Error: Unable to find a match: ffmpeg 添加RPMfusion仓库 yum install https://download1.rpmfus…