知识点:代理设计模式

news/2024/10/21 2:11:06/

1.场景设定和问题复现

   1 准备项目

       pom.xml

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.3.1</version>
    <scope>test</scope>
</dependency>

   2. 声明功能接口

/**
 * 功能接口
 */
public interface GongNeng {
    //吃饭
    void chifan();
    //谈小目标
    void tanxiaomb();
}

3.声明老总目标类实现功能接口

/**
 * 老总:目标对象类实现功能接口
 */
public class LaoZong implements GongNeng {
    @Override
    public void chifan() {
        //核心功能
        System.out.println("老总吃饭...");
    }

    @Override
    public void tanxiaomb() {
        //核心功能
        System.out.println("老总谈一个亿的小目标...");
    }
}

  

4.声明带预约和联系方式附加功能实现

   新需求: 需要在每个方法中,添加控制台输出,输出预约和输出留个联系方式,方便下次联系!

/**
 * 老总:目标对象类
 * 需要在每个方法中,添加控制台输出,输出预约和输出留个联系方式,方便下次联系!
 */
public class LaoZong implements GongNeng {
    @Override
    public void chifan() {
        System.out.println("预约....");
        //核心功能
        System.out.println("老总吃饭...");
        System.out.println("留个联系方式,方便下次联系....");
    }

    @Override
    public void tanxiaomb() {
        System.out.println("预约....");
        //核心功能
        System.out.println("老总谈一个亿的小目标...");
        System.out.println("留个联系方式,方便下次联系....");
    }
}

   

5.代码问题分析

1. 代码缺陷

    - 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力

    - 附加功能代码重复,分散在各个业务功能方法中!冗余,且不方便统一维护!

2. 解决思路

      核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。

      将重复的代码统一提取,并且[[动态插入]]到每个业务方法!

3. 技术困难

    解决问题的困难:提取重复附加功能代码到一个类中,可以实现

    但是如何将代码插入到各个方法中,我们不会,我们需要引用新技术!!!

6.解决技术代理模式

   1. 代理模式

二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

  2. 无代理场景

  3. 有代理场景

   4. 生活中的代理

- 广告商找大明星拍广告需要经过经纪人

- 合作伙伴找大老板谈合作要约见面时间需要经过秘书

- 房产中介是买卖双方的代理

- 太监是大臣和皇上之间的代理

  5. 相关术语

- 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。(中介)

   动词:指做代理这个动作,或这项工作

   名词:扮演代理这个角色的类、对象、方法

- 目标:**被代理**“套用”了核心逻辑代码的类、对象、方法。(房东)

代理在开发中实现的方式具体有两种:静态代理,[动态代理技术]

   6.静态代理实现

     1. 主动创建代理类

/**
 * 秘书:静态代理类
 */
public class XiaoMi implements GongNeng{
    //将被代理的目标对象:老总
    LaoZong laoZong;

    public XiaoMi(LaoZong laoZong) {
        this.laoZong = laoZong;
    }

    @Override
    public void chifan() {
        //附加功能由代理类中的代理方法来实现:核心业务前执行的操作
        System.out.println("预约....");
        //调用目标对象老总的吃饭方法
        laoZong.chifan();
        //附加功能由代理类中的代理方法来实现:核心业务后执行的操作
        System.out.println("留个联系方式,方便下次联系....");
    }

    @Override
    public void tanxiaomb() {
        //附加
        System.out.println("预约....");
        laoZong.tanxiaomb();
        //附加
        System.out.println("留个联系方式,方便下次联系....");
    }
}

  测试代码

@Test
public void  testStaticProxy(){
    //创建老总对象:马云
    LaoZong yunyun=new LaoZong();
    //创建云云的秘书
    XiaoMi xm=new XiaoMi(yunyun);
    //吃饭
    xm.chifan();
    System.out.println("--------------");
    xm.tanxiaomb();
}

    静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。

提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

7.动态代理

   1. 动态代理技术分类

动态代理是一种在运行时动态生成代理对象的技术。它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。

JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口!他会根据目标类的接口动态生成一个代理对象!代理对象和目对象有标相同的接口!(拜把子)

cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口!(认干爹)

   2.基于jdk代理技术实现

        1.声明功能接口

/**
 * 功能接口
 */
public interface GongNeng {
    //吃饭
    void chifan();
    //谈小目标
    void tanxiaomb();
}

        2.声明老总目标类实现功能接口

/**
 * 老总:目标对象类实现功能接口
 */
public class LaoZong implements GongNeng {
    @Override
    public void chifan() {
        //核心功能
        System.out.println("老总吃饭...");
    }

    @Override
    public void tanxiaomb() {
        //核心功能
        System.out.println("老总谈一个亿的小目标...");
    }
}

   

          3.定义jdk动态代理工厂类,生成动态代理对象和目标对象实现同一个接口,并调用代理方法invoke

/**
 * jdk动态代理工厂类,生成小秘动态代理对象
 */
public class JdkProxyHandler implements InvocationHandler {
    //代理的目标对象
    LaoZong laoZong;

    public JdkProxyHandler(LaoZong laoZong) {
        this.laoZong = laoZong;
    }

    /**
     * invoke()设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
     * proxy:代理对象
     * method:代理对象需要实现的方法,即其中需要重写的方法
     * argsmethod所对应方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("预约....");
        //调用目标对象的方法
        Object obj=method.invoke(laoZong,args);
        System.out.println("留下联系方式,方便下次联系....");
        return obj;
    }
}

           4. 测试代码

/**
 * JDK动态代理
 */
@Test
public void testJdkProxy(){
    //创建老总对象
    LaoZong yunyun=new LaoZong();
    /**
     * 创建代理对象,通过jdk动态代理生成代理对象的方法
     * Proxy.newProxyInstance():创建一个代理实例
     * 其中有三个参数:
     * 1classLoader:加载动态生成的代理类的类加载器
     * 2interfaces:目标对象实现的所有接口的class对象所组成的数组
     * 3invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
     */
    GongNeng xm=(GongNeng)Proxy.newProxyInstance(TestProxy.class.getClassLoader(),new Class[]{GongNeng.class},new JdkProxyHandler(yunyun));
    //通过代理对象调用目标对象的方法,从而扩展附加功能
    xm.chifan();
    System.out.println("--------------");
    xm.tanxiaomb();
}

 

3. 基于cglib代理技术,生成动态代理对象

   1.引入cglib坐标

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

   2.定义目标对象的类 (不要实现接口)

/**
 * 老总:目标对象类
 */
public class LaoZong {
    public void chifan() {
        //核心功能
        System.out.println("老总吃饭...");
    }
    public void tanxiaomb() {
        //核心功能
        System.out.println("老总谈一个亿的小目标...");
    }
}

    3.定义cglib动态代理工厂类,生成动态代理对象,是目标对象的子类。

      并调用代理方法invoke

/**
 * cglib动态代理工厂类,生成日志动态代理对象
 */
public class CglibInterceptor implements MethodInterceptor {
    /**
     * intercept设置代理对象实现目标对象方法的过程,即代理类中如何重写目标中的方法
     * o :目标对象
     * method:目标对象的方法
     * MethodProxy:代理对象的方法
     * objectsmethod所对应方法的参数
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("预约....");
        //调用目标对象的方法
        Object obj=methodProxy.invokeSuper(o,objects);
        System.out.println("留下联系方式,方便下次联系....");
        return obj;
    }
}

      3. 测试代码

/**
 * Cglib动态代理
 */
@Test
public void testCglibProxy(){
    //创建代理对象,是目标对象的子类
    Enhancer e=new Enhancer();
    //1.指定父类:目标对象
    e.setSuperclass(LaoZong.class);
    //2.指定callback,指定代理对象调用一个对象的代理方法
    e.setCallback(new CglibInterceptor());
    LaoZong xm=(LaoZong) e.create();
    //通过代理对象调用目标对象的方法,从而扩展附加功能
    xm.chifan();
    System.out.println("--------------");
    xm.tanxiaomb();
}

     注意:运行时会出现异常,原因由于jdk8之后的版本的反射相关功能被限制,导致了异常

     解决方案:编辑配置时手动添加两个参数实现jdk的兼容,选择开启不被允许的反射功能。--add-opens java.base/java.lang=ALL-UNNAMED

   8.代理总结

**代理方式可以解决附加功能代码干扰核心代码和不方便统一维护的问题!**

他主要是将附加功能代码提取到代理中执行,不干扰目标核心代码!

但是我们也发现,无论使用静态代理和动态代理(jdk,cglib),程序员的工作都比较繁琐!

需要自己编写代理工厂等!

但是,提前剧透,我们在实际开发中,不需要编写代理代码,我们可以使用[Spring AOP]框架,他会简化动态代理的实现!!!


http://www.ppmy.cn/news/1540662.html

相关文章

兰迪·舍克曼担任生命银行链(LBC)顾问,赋能基因数据区块链技术发展

兰迪舍克曼&#xff08;Randy Schekman&#xff09;作为生命银行链&#xff08;Life Bank Chain, LBC&#xff09;的顾问参与其中&#xff0c;这无疑是个令人兴奋的消息&#xff01;他在生理医学和基因研究方面拥有深厚的专业知识&#xff0c;必将对LBC的使命&#xff0c;即安全…

10-14到10-16学习笔记

mybatis-plus设置逻辑删除 1.配置全局的逻辑删除规则&#xff08;高版本可以省略&#xff09; 2.配置逻辑删除的组件bean(高版本可以省略) 3.给实体类的属性加上逻辑删除TableLogic注解 Element-ui的Dialog 对话框使用 <el-dialog :title"title" :visible.sync…

FPGA实现SPI接口,用verilog实现,SPI接口使用例程!!!

SPI接口详解 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种高速、全双工、同步的通信总线。它常用于连接微控制器和各种外围设备&#xff0c;如EEPROM、FLASH、AD转换器等。SPI接口主要具有以下优点&#xff1a; 全双工通信&#xff1a;支持同时发送和接收数…

【Java并发编程】线程池的四种拒绝策略(饱和策略)

引入 线程池的拒绝策略是当线程池出现以下情况时&#xff0c;由于线程池达到其容量上限而无法接受新任务时的处理机制&#xff1a; 线程池已满&#xff1a;当线程池中的所有线程都在执行任务时&#xff0c;新提交的任务无法立即执行。这种情况发生在当前线程池的核心线程和最…

卷积神经网络(CNN)-Padding介绍

在卷积过程中,输出特征图的大小由输入特征图的大小、内核的大小和步幅决定。如果我们简单地在输入特征图上应用内核,那么输出特征图将小于输入。这可能会导致输入特征图边界处的信息丢失。为了保留边框信息,我们使用padding。 什么是填充 Padding是一种技术,用于在对特征…

C++ 类的基础用法与详细说明:简单易懂的入门指南

什么是类&#xff1f; C类_百度百科 类是C中一种用于封装数据和功能的基本结构。你可以将类视为一种自定义的数据类型&#xff0c;它可以包含数据&#xff08;成员变量&#xff09;和操作这些数据的函数&#xff08;成员函数&#xff09;。 创建一个简单的类 让我们通过一个…

【Golang】踩坑记录:make()创建引用类型,初始值是不是nil!!

文章目录 起因二、得记住的知识点1. make()切片&#xff0c;初始化了吗&#xff1f;2. make()切片不同长度容量&#xff0c;append时的差别3. 切片是指向数组的指针吗&#xff1f;4. 切片扩容时&#xff0c;重新分配内存&#xff0c;原切片的数据怎么办&#xff1f; 三、咳咳&a…

CLion和Qt 联合开发环境配置教程(Windows和Linux版)

需要安装的工具CLion 和Qt CLion下载链接 :https://www.jetbrains.com.cn/clion/ 这个软件属于直接默认安装就行&#xff0c;很简单&#xff0c;不多做介绍了 Qt:https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/ window 直接点exe Linux 先c…