当前位置: 首页 > article >正文

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

代理设计模式

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.kler.cn/news/156880.html

相关文章:

  • rar压缩包如何分卷压缩
  • 全网最新最全的自动化测试教程:python+pytest接口自动化-请求参数格式的确定
  • Java的+号详解 与 字符串拼接的底层逻辑
  • 二阶变系数线性微分方程
  • 光伏设计方案中最重要的是什么?
  • 2、Linux_远程操作
  • 深入了解Java Duration类,对时间的精细操作
  • 龙迅#LT6911GX是一款高性能HDMI2.1至MIPI或LVDS芯片,支持图像处理,DSC压缩和嵌入式LPDDR4 旋转功能!
  • 【springboot原理篇】Bean的加载方式,面试必看
  • sprintf VS snprintf 函数
  • Realtek蓝牙Android10.0移植结束后的基本测试和常见问题分析
  • Linux halt命令教程:如何安全地关闭你的系统(附详细实例和注意事项)
  • 浅谈基于能耗评价指标的医院智能配电能效管理分析
  • 新版IDEA中,module模块无法被识别,类全部变成咖啡杯无法被识
  • Leetcode 538 把二叉搜索树转换为累加树
  • 管理Android12系统的WLAN热点
  • OpenAI发布一周年,那些声称超过它的模型都怎么样了?
  • 如何知道B站各分区直播数据趋势?
  • MySQL进阶_EXPLAIN重点字段解析
  • 语音芯片的BUSY状态指示功能特征:提升用户体验与系统稳定性的关键
  • JAVA Spring boot Process finished with exit code 0
  • golang channel执行原理与代码分析
  • 基于Langchain的txt文本向量库搭建与检索
  • 菜鸟学习日记(python)——数据类型转换
  • 记一次ThreadPoolTaskExecutor的坑
  • 2023年道路运输企业主要负责人证模拟考试题库及道路运输企业主要负责人理论考试试题
  • IRS辅助的隐蔽通信 (IRS aided covert communication)
  • csapp-linklab之第3阶段“输出学号”实验报告(强弱符号)
  • qt 安装
  • [C/C++]数据结构 堆排序(详细图解)