【Android面试八股文】你能说一说什么是代理模式?静态代理和动态代理分别是什么?如何实现?

news2024/11/6 9:58:42

文章目录

  • 一、代理模式
    • 1.1 代理模式概念
    • 1.2 代理模式的目的
    • 1.3 代理模式的三个角色
    • 1.4 代理模式的两种实现方式
    • 1.5 代理模式的优点
    • 1.6 代理模式的缺点
    • 1.7 适用场景
  • 二、静态代理
    • 2.1 静态代理
    • 2.2 动态代理
      • 2.2.1 JDK动态代理
      • 2.2.2 CGLIB动态代理
      • 2.2.3 JDK 动态代理和 CGLIB 动态代理对比

一、代理模式

1.1 代理模式概念

代理模式(Proxy Pattern),属于结构型模式。使用一个类代表另一个类的功能,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

比如生活中常见的中介与租房,租房者不需要直接与房东交互,房东把房屋租赁交给中介代理。

1.2 代理模式的目的

代理模式的目的有:

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

1.3 代理模式的三个角色

代理模式一般会有三个角色:
在这里插入图片描述

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

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

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

1.4 代理模式的两种实现方式

代理模式可以分为静态代理和动态代理两种实现方式:
在这里插入图片描述
在这里插入图片描述

  • 静态代理
    在编译时已经确定代理类的具体实现,需要为每个被代理类编写一个代理类。

    静态代理不灵活,但易于理解和实现。

  • 动态代理
    在运行时动态创建代理对象,无需为每个被代理类编写单独的代理类。

    Java 提供了 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来支持动态代理,通过这些类可以在运行时生成代理对象

1.5 代理模式的优点

  • 代理对象可以隐藏原始对象的实现细节,使得客户端无需了解原始对象的具体实现。
  • 代理对象可以在原始对象的基础上添加额外的功能,例如缓存、安全验证等。
  • 代理对象可以控制对原始对象的访问,保护原始对象不被非法访问。
  • 代理对象可以在客户端和原始对象之间起到中介作用,使得客户端与原始对象之间的耦合度降低。

1.6 代理模式的缺点

  • 引入代理类会增加系统的复杂性,增加了学习和理解的成本。
  • 由于增加了代理层,导致请求处理速度变慢。

1.7 适用场景

代理模式主要适用于需要控制、增强或隐藏对象访问的场景。适合代理访问的具体场景如下:

  • 远程代理:当客户端需要访问远程对象(位于不同地址空间或网络中)时,可以使用代理模式来隐藏底层网络通信的复杂性,代理对象负责处理网络通信,并将结果返回给客户端。
  • 虚拟代理:当创建和初始化对象的开销很大时,可以使用代理模式延迟对象的实例化,只有在需要真正使用对象时才进行初始化。这样可以提高系统的性能和资源利用率。
  • 安全代理:代理模式可以用于控制对敏感资源的访问,代理对象可以验证客户端的权限或者在访问资源前执行一些安全检查,从而保护真实对象。
  • 日志记录代理:通过代理模式,我们可以在真实对象的方法执行前后进行日志记录,以实现日志记录、调试和性能监测等功能。
  • 延迟加载代理:当需要使用的对象具有较大的开销时,可以使用代理模式来实现延迟加载,只有在真正需要时才加载对象,以节省资源和提高响应速度。
  • 缓存代理:代理模式可以用于实现对象的缓存,当客户端请求某个对象时,代理对象先检查缓存中是否存在该对象,如果存在则直接返回,否则创建新对象并缓存起来,从而提高系统性能。

二、静态代理

2.1 静态代理

静态代理是在编译时确定代理类的具体实现,即代理类的源代码在编译期间就已经确定。通常情况下,需要手动编写代理类,且代理类与委托类实现同样的接口或继承同样的父类。静态代理的实现比较简单,但是当需要代理的类较多时,会导致代理类数量庞大,维护困难。

以下是静态代理的示例代码:

// 定义接口
interface Subject {
    void doAction();
}

// 目标对象类
class RealSubject implements Subject {
    @Override
    public void doAction() {
        System.out.println("RealSubject: executing action.");
    }
}

// 代理对象类
class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doAction() {
        System.out.println("ProxySubject: before action.");
        realSubject.doAction();
        System.out.println("ProxySubject: after action.");
    }
}

// 使用代理对象
public class StaticProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxy = new ProxySubject(realSubject);
        proxy.doAction();
    }
}

2.2 动态代理

2.2.1 JDK动态代理

动态代理是在运行时动态生成代理类的方式,无需手动编写代理类。Java 提供了 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来支持动态代理。通过 Proxy.newProxyInstance 方法创建代理对象,并传入一个实现了 InvocationHandler 接口的对象,动态代理对象会在运行时生成。

以下是动态代理的示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Subject {
    void doAction();
}

// 目标对象类
class RealSubject implements Subject {
    @Override
    public void doAction() {
        System.out.println("RealSubject: executing action.");
    }
}

// 动态代理处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

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

// 使用动态代理
public class DynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new DynamicProxyHandler(realSubject)
        );
        proxy.doAction();
    }
}

在动态代理示例中,Proxy.newProxyInstance 方法会根据指定的类加载器、接口和 InvocationHandler 对象动态生成代理对象,实现了对目标对象的动态代理。

2.2.2 CGLIB动态代理

首先,你需要在项目中添加CGLIB的依赖。如果你使用的是Maven,可以在pom.xml文件中添加以下依赖:

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

接下来,我们创建一个接口Subject,它代表真实对象和代理对象之间的共享接口:

public interface Subject {
    void doSomething();
}

然后,创建一个实现了Subject接口的真实对象RealSubject类:

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject: 正在做一些事情...");
    }
}

接下来,创建一个实现了CGLIB的MethodInterceptor接口的ProxyInterceptor类:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ProxyInterceptor implements MethodInterceptor {

    private Object target;

    // 构造方法,接受一个目标对象
    public ProxyInterceptor(Object target) {
        this.target = target;
    }

    // 拦截方法调用
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("方法调用前");
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        System.out.println("方法调用后");
        return result;
    }
}

最后,我们创建一个主类Main来演示如何使用CGLIB进行动态代理:

import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        // 创建真实对象
        RealSubject realSubject = new RealSubject();

        // 创建拦截器
        ProxyInterceptor interceptor = new ProxyInterceptor(realSubject);

        // 使用CGLIB的Enhancer来创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class); // 设置代理类的父类为真实对象类
        enhancer.setCallback(interceptor); // 设置回调为我们的拦截器

        // 创建代理对象
        Subject proxySubject = (Subject) enhancer.create();
        
        // 调用方法,通过代理对象
        proxySubject.doSomething();
    }
}

运行Main类时,它将使用CGLIB创建一个代理对象,并通过代理对象调用doSomething()方法。ProxyInterceptor将拦截方法调用,在方法调用前后执行一些操作,然后将实际的方法调用委托给真实对象。

这个例子演示了如何使用CGLIB来实现动态代理,允许我们在不修改原始类的情况下增强其功能。希望这个解释对你理解CGLIB动态代理有所帮助。

2.2.3 JDK 动态代理和 CGLIB 动态代理对比

JDK动态代理CGLIB动态代理是两种常见的Java动态代理实现方式,它们在实现原理、适用场景和特点上有一些区别。

  1. 实现原理:

    • JDK动态代理:基于接口的动态代理。JDK动态代理要求目标类必须实现一个或多个接口,它利用Java的反射机制生成目标类的代理对象,代理对象实现了目标接口,并且可以拦截接口方法的调用。
    • CGLIB动态代理:基于继承的动态代理。CGLIB通过继承目标类并重写其方法来实现代理,因此不要求目标类必须实现接口。
  2. 适用场景:

    • JDK动态代理适用于要代理的类实现了接口的情况,因为JDK动态代理生成的代理对象实现了接口,可以被强制类型转换为接口类型。
    • CGLIB动态代理适用于要代理的类没有实现接口的情况,或者你希望在运行时创建目标类的子类来作为代理。
  3. 性能表现:

    • 一般情况下,JDK动态代理比CGLIB动态代理更快,因为JDK动态代理是基于Java自带的反射包实现的,而CGLIB则需要通过ASM字节码处理库来生成代理类。
    • 但是,对于目标类没有实现接口的情况,使用CGLIB可以实现代理,而JDK动态代理则无法对非接口类进行代理。

总的来说,如果目标类已经实现了接口,并且你希望代理对象是强制类型转换后的接口类型,那么可以选择JDK动态代理;如果目标类没有实现接口,或者你希望在运行时创建目标类的子类来作为代理,那么可以选择CGLIB动态代理。

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

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

相关文章

Web应用安全测试-权限缺失

Web应用安全测试-权限缺失 Flash跨域访问 漏洞描述&#xff1a;flash跨域通信&#xff0c;依据的是crossdomain.xml文件。该文件配置在服务端&#xff0c;一般为根目录下&#xff0c;限制了flash是否可以跨域获取数据以及允许从什么地方跨域获取数据。举个例子&#xff1a; 1、…

教育界杂志教育界杂志社教育界编辑部2024年第13期目录

教育视界 “三全育人”视角下九年一贯制学校德育体系构建与探索 练成; 2-4 儿童审美视角下小学文言文教学的实践研究 张瑾; 5-7 打造初中美术创作教学的“四度空间” 叶才红; 8-10 探索之窗《教育界》投稿&#xff1a;cn7kantougao163.com “屋顶农场”项目迭代…

FinalReference 如何使 GC 过程变得拖拖拉拉

本文基于 OpenJDK17 进行讨论&#xff0c;垃圾回收器为 ZGC。 提示&#xff1a; 为了方便大家索引&#xff0c;特将在上篇文章 《以 ZGC 为例&#xff0c;谈一谈 JVM 是如何实现 Reference 语义的》 中讨论的众多主题独立出来。 FinalReference 对于我们来说是一种比较陌生的 R…

python安装包中的.dist-info作用

在使用pip install 包名 进行python第三方库的时候&#xff0c;安装完库之后通常会出现一个库名&#xff0c;还有一个.dist-info的文件&#xff0c;以安装yolov8所依赖的框架ultralytics为例&#xff0c;成功安装后会出现以下文件夹&#xff1a; 第一个ultralytics是概该框架包…

python实践笔记(三): 异常处理和文件操作

1. 写在前面 最近在重构之前的后端代码&#xff0c;借着这个机会又重新补充了关于python的一些知识&#xff0c; 学习到了一些高效编写代码的方法和心得&#xff0c;比如构建大项目来讲&#xff0c;要明确捕捉异常机制的重要性&#xff0c; 学会使用try...except..finally&…

做恒指交易一定要有耐心

1、记住成为赢利的交易者是一个旅程&#xff0c;而非目的地。世界上并不存在只赢不输的交易者。试着每天交易的更好一些&#xff0c;从自己的进步中得到乐趣。聚精会神学习技术分析的技艺&#xff0c;提高自己的交易技巧&#xff0c;而不是仅仅把注意力放在自己交易输赢多少上。…

轮式机器人Swiss-Mile城市机动性大提升:强化学习引领未来城市物流

喜好儿小斥候消息&#xff0c;苏黎世联邦理工学院的研究团队成功开发了一款革命性的机器人控制系统&#xff0c;该系统采用强化学习技术&#xff0c;使轮式四足机器人在城市环境中的机动性和速度得到了显著提升。 喜好儿网 这款专为轮腿四足动物设计的控制系统&#xff0c;能…

eNSP学习——配置基于接口地址池的DHCP

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、基于接口配置 DHCP Server 功能 3、配置基于接口的DHCP Server租期/DNS服务器地址 4、配置 DHCP Client 主要命令 //查看DHCP地址池中的地址分配情况 display ip pool//开启D…

【源码】2024运营版多商户客服系统/在线客服系统/手机客服/PC软件客服端

带客服工作台pc软件源代码&#xff0c;系统支持第三方系统携带参数打开客服链接&#xff0c;例如用户名、uid、头像等 支持多商家&#xff08;多站点&#xff09;支持多商家&#xff08;多站点&#xff09;&#xff0c;每个注册用户为一个商家&#xff0c;每个商家可以添加多个…

30.保存游戏配置到文件

上一个内容&#xff1a;29.添加录入注入信息界面 以 29.添加录入注入信息界面 它的代码为基础进行修改 效果图&#xff1a; 首先在我们辅助程序所在目录下创建一个ini文件 文件内容 然后首先编写一个获取辅助程序路径的代码 TCHAR FileModule[0x100]{};GetModuleFileName(NUL…

嵌入式学习记录6.17(qss练习)

一思维导图 二.练习 widget.h #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowFlag(Qt::FramelessWindowHint);this->setAttribute(Qt:…

Java多线程设计模式之保护性暂挂模式

模式简介 多线程编程中&#xff0c;为了提高并发性&#xff0c;往往将一个任务分解为不同的部分。将其交由不同的线程来执行。这些线程间相互协作时&#xff0c;仍然可能会出现一个线程等待另一个线程完成一定的操作&#xff0c;其自身才能继续运行的情形。 保护性暂挂模式&a…

数据治理服务解决方案(35页WORD)

方案介绍&#xff1a; 本数据治理服务解决方案旨在为企业提供一站式的数据治理服务&#xff0c;包括数据规划、数据采集、数据存储、数据处理、数据质量保障、数据安全及合规等方面。通过构建完善的数据治理体系&#xff0c;确保企业数据的准确性、完整性和一致性&#xff0c;…

Excel 识别数据层次后转换成表格

某列数据可分为 3 层&#xff0c;第 1 层是字符串&#xff0c;第 2 层是日期&#xff0c;第 3 层是时间&#xff1a; A1NAME122024-06-03304:06:12404:09:23508:09:23612:09:23717:02:2382024-06-02904:06:121004:09:231108:09:2312NAME2132024-06-031404:06:121504:09:231620…

JPS(Jump Point Search)跳点搜索路径规划算法回顾

本篇文章主要回顾一下几年前学的JPS跳点搜索规划算法的相关内容&#xff0c;之前学的时候没有进行概括总结&#xff0c;现在补上 一、A*算法简单回顾 – 1、基本介绍和原理 A*&#xff08;A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法&#xff0c;也是解决许多…

RERCS系统开发实战案例-Part06 FPM Application添加列表组件(List UIBB)

在FPM Application中添加搜索结果的List UIBB 1&#xff09;添加List UIBB 2&#xff09;提示配置标识不存在&#xff0c;则需要新建配置标识&#xff08;* 每个组件都必须有对应的配置标识&#xff09;&#xff1b; 3&#xff09;选择对应的包和请求 4&#xff09;为List UIB…

简述spock以及使用

1. 介绍 1.1 Spock是什么&#xff1f; Spock是一款国外优秀的测试框架&#xff0c;基于BDD&#xff08;行为驱动开发&#xff09;思想实现&#xff0c;功能非常强大。Spock结合Groovy动态语言的特点&#xff0c;提供了各种标签&#xff0c;并采用简单、通用、结构化的描述语言…

【软件测试】软件测试入门

软件测试入门 一、什么是软件测试二、软件测试和软件开发的区别三、软件测试在不同类型公司的定位1. 无组织性2. 专职 OR 兼职3. 项目性VS.职能性4.综合型 四、一个优秀的软件测试人员具备的素质1. 技能相关2. 非技能相关 一、什么是软件测试 最常见的理解是&#xff1a;软件测…

设备保养计划不再是纸上谈兵,智能系统让执行更到位!

在物业管理的日常工作中&#xff0c;我们常常听到“设备保养台账”“设备保养计划”“设备保养记录”等等这些词&#xff0c;但你是否真正了解它们的含义&#xff1f;是否知道一个完善的设备保养计划、记录、台账对于物业运营的重要性&#xff1f;今天&#xff0c;我们就来深入…

AI产品经理,应掌握哪些技术?

美国的麻省理工学院&#xff08;Massachusetts Institute of Technology&#xff09;专门负责科技成果转化商用的部门研究表明&#xff1a; 每一块钱的科研投入&#xff0c;需要100块钱与之配套的投资&#xff08;人、财、物&#xff09;&#xff0c;才能把思想转化为产品&…