当前位置: 首页 > 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/a/156880.html

相关文章:

  • 区块链技术在电子政务中的应用
  • 谷歌浏览器的自动翻译功能如何开启
  • MySQL远程连接错误解决:Host is not allowed to connect to this MySQL server
  • html+js+css实现拖拽式便签留言
  • 丹摩征文活动 | 丹摩智算:大数据治理的智慧引擎与实践探索
  • 如何选择最适合的自闭症干预机构?
  • 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状态指示功能特征:提升用户体验与系统稳定性的关键