Java 代理模式之静态代理与动态代理

2023-09-13 16:57:55

1,代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

代理模式的目的:

(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;

(2)通过代理对象对访问进行控制;

代理模式一般会有三个角色:

抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口。

真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。

代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。

2,静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

比如我要卖房子,但是我不会自己去卖,我会先找到中介,让中介去卖。

这里的抽象角色就是卖房子。

真实角色就是我卖房子。

代理角色就是中介卖房子。

新建一个抽象接口:

public interface SailRoom {

    //卖房子
    void sailRoom();
}

创建真实的卖房子的对象:

public class Me implements SailRoom{

    @Override
    public void sailRoom() {
        System.out.println("sail my room");
    }
}

创建一个中介代理类:

public class RoomProxy implements SailRoom {

    private Me me;

    public RoomProxy(Me me) {
        this.me = me;
    }

    private void before(){
        System.out.println("before sail room");
    }

    private void after(){
        System.out.println("after sail room");
    }

    @Override
    public void sailRoom() {
        before();
        me.sailRoom();
        after();
    }
}

在应用时这么使用:

RoomProxy proxy =new RoomProxy(new Me());
proxy.sailRoom();

那我如果要想再卖一个二手苹果手机,那我找中介就不行了,那我只能再去创建一个新的卖二手手机代理,去去卖二手手机。

创建一个新的卖手机的接口:

public interface SailPhone {
    /*
    * 卖手机
    * */
    void sailPhone();
}

实现这个接口: 

public class Me implements SailRoom,SailPhone{

    @Override
    public void sailRoom() {
        System.out.println("sail my room");
    }

    @Override
    public void sailPhone() {
        System.out.println("sail my phone");
    }
}

 创建一个卖手机的代理类:

public class PhoneProxy implements SailPhone{
    
    private Me me;
    
    private void before(){
        System.out.println("before sail phone");
    }
    
    private void after(){
        System.out.println("after sail phone");
    }
    
    @Override
    public void sailPhone() {
        before();
        me.sailPhone();
        after();
    }
}

使用时:

public static void main(String[] args) {
    Me me =new Me();
    RoomProxy proxy =new RoomProxy(me);
    PhoneProxy phoneProxy =new PhoneProxy(me);
    proxy.sailRoom();
    phoneProxy.sailPhone();
}

输出: 

before sail room
sail my room
after sail room
before sail phone
sail my phone
after sail phone

从上面可以很明显的看出静态代理模式的缺点:

1,如果有多个类需要代理,那么就需要创建多个代理类分别代理目标对象,工作量较大,不利于维护。

2,当接口的方法增加或修改的时候,很多类都需要修改。因为,目标类和代理类都实现了相同的接口

3,动态代理

上面那个案例,用动态代理怎么实现呢?

下面我们用jdk的动态代理来实现:

先创建我们的动态代理类:

public class SailProxy implements InvocationHandler {

    private Object object;

    public SailProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("do before");
        Object invoke = method.invoke(object, args);
        System.out.println("do after");
        return invoke;
    }
}

在使用时:

public class Test {

    public static void main(String[] args) {

        Me me =new Me();
        SailProxy sailProxy =new SailProxy(me);
        Object o =Proxy.newProxyInstance(me.getClass().getClassLoader(), new Class[]{SailRoom.class,SailPhone.class}, sailProxy);

        SailRoom sailRoom = (SailRoom) o;
        sailRoom.sailRoom();
        SailPhone sailPhone = (SailPhone) o;
        sailPhone.sailPhone();

    }
}

输出:

do before
sail my room
do after
do before
sail my phone
do after

这样我们就实现了一劳永逸,只创建一个代理类,就可以代理无数个接口。没错,是接口,JDK的实现的动态代理,它代理的只是接口。

下面我们就来详细讲解一下上个案例。

首先,在SailProxy中,我们实现了InvocationHandler这个接口,实现了接口的invoke方法。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("do before");
    Object invoke = method.invoke(object, args);
    System.out.println("do after");
    return invoke;
}

其中 参数一Object proxy 与:

Object o =Proxy.newProxyInstance(me.getClass().getClassLoader(), new Class[]{SailRoom.class,SailPhone.class}, sailProxy);

的返回值Object o是一个对象,都是代表的传入的 new Class[]{SailRoom.class,SailPhone.class}

的接口对象。可以强转为SailRoom也可以强转为SailPhone。

接下来看第二个参数Method method,它代表的是你调用的接口的方法,可以是sailRoom(),也可以是sailPhone()。

第三个参数Object[] args就是接口方法的参数。

当你调用sailRoom.sailRoom()时,就会回调到invoke方法里面,调用

此时的method就是sailRoom(),object就是Me这个实际的角色,args就是方法参数,此处为空。

注意:

1,jdk实现的动态代理只能代理接口,不能代理类。

2,动态代理生成的代理类并不像普通类那样通过Javac生成class文件存放在磁盘中,他并不是一个真正的class文件,它只是存放在内存中。

更多推荐

Leetcode算法入门与数组丨5. 数组二分查找

文章目录1二分查找算法2二分查找细节3二分查找两种思路3.1直接法3.2排除法1二分查找算法二分查找算法是一种常用的查找算法,也被称为折半查找算法。它适用于有序数组的查找,并通过将待查找区间不断缩小一半的方式来快速定位目标值。算法思想如下:首先,确定待查找数组的起始位置(通常为数组的第一个元素)和结束位置(通常为数组的

微服务08-认识和使用SpringAMQP

1.AMQP的认识1.1介绍AMQP是什么?看完你就知道了_hello_读书就是赚钱的博客-CSDN博客_amqp好处:什么connection:消息队列的连接、channel:服务发送接收消息的通道、Queue:消息队列——>这些你都不需要自己编写工作过程:发布者(Publisher)发布消息(Message),经由

常见的查找算法以及分块搜索算法的简明教程

顺序查找最基本的查找算法举例//顺序查找publicstaticintsearchSequence(int[]arr,inttarget){inti=0;for(intarr2:arr){if(arr2==target){returni;}i++;}return-1;}二分查找[!warning]值得注意的是这个二分查

Go的性能优化建议

前言:\textcolor{Green}{前言:}前言:💞这个专栏就专门来记录一下寒假参加的第五期字节跳动训练营💞从这个专栏里面可以迅速获得Go的知识Go的性能优化建议3性能优化建议3.1性能优化建议-Benchmark3.2性能优化建议-slice3.3性能优化建议-Map3.4性能优化建议-字符串处理3.5性能

【AI视野·今日CV 计算机视觉论文速览 第248期】Mon, 18 Sep 2023

AI视野·今日CS.CV计算机视觉论文速览Mon,18Sep2023Totally83papers👉上期速览✈更多精彩请移步主页Interesting:📚Robuste-NeRF,处理高速且大噪声事件相机流的NERF模型。(fromNUS新加坡国立)稀疏噪声事件与稠密事件数据的区别:模型架构:项目网站:https:

Python网络编程(socket)

网络编程指的是:在程序中实现两台计算机之间的通信。Python提供了大量网络编程的工具和库,本文重点学习socket和select模块。网络编程涉及许多关于TCPIP的基础知识,本文默认对这些知识已经了解了,不再对TCPIP相关的知识进行学习。socket模块这个模块提供了访问BSD套接字的接口。在所有现代Unix系统

开始在 Windows 上将 Python 用于 Web 开发

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录设置开发环境安装适用于Linux的Windows子系统设置VisualStudioCode创建新项目安装Python、pip和venv创建虚拟环境打开WSL-Remote窗口安装MicrosoftPython扩

【C# 基础精讲】抽象类与接口

抽象类(AbstractClass)和接口(Interface)是面向对象编程中两种重要的概念,它们用于定义类的结构、行为和关系,是实现多态性、代码复用和系统设计的关键手段。在C#及其他面向对象编程语言中,抽象类和接口都发挥着重要作用。本文将详细解释抽象类和接口的概念、特点、用法以及在C#中的应用。1.抽象类的概念与特

同城信息服务源码 本地生活服务小程序源码

同城信息服务源码本地生活服务小程序源码功能介绍:基本设置:网站参数、安全设置、分站管理、支付设置、操作日志、地区设置、公交地铁、国际区号、清理缓存、模板风格、模块管理、域名管理、底部菜单、消息通知、登录设置其他设置:关键词管理、单页文档、网站公告、帮助信息、网站协议、广告设置、友情链接、举报管理、意见反馈、投诉列表短信

CH573-09-BLE蓝牙安卓应用二次开发——RISC-V内核BLE MCU快速开发教程

一、基础工程搭建在上一章最后一讲的BLE蓝牙例程中,我们使用了沁恒官方的BLE调试助手完成数据发送,接下来我们使用AndroidStudio完成一款简易的BLE调试助手。1、参考文章我这里参考了CSDN中的一位博主“摸爬滚打的程序媛”的文章以及对应文章中的AndroidStudioBLE应用工程的Demo。版权声明:链

MP3算法及代码例程

MP3(MPEG-1AudioLayerIII)是一种数字音频压缩算法,用于对音频进行高效的压缩。MP3算法能够显著减小音频文件的大小,同时保持较高的音质。以下是MP3算法的主要步骤:采样率转换:将输入音频信号的采样率转换为固定的值,通常为44.1kHz。这是因为人耳对于音频的感知范围大约在20Hz到20kHz之间,因

热文推荐