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

文章目录

  • 一、代理模式
    • 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动态代理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】122、RNN面临哪些问题?

循环神经网络&#xff08;RNN&#xff09;主要面临梯度消失和梯度爆炸两个核心问题&#xff0c;这严重影响了其处理长期依赖的能力。此外&#xff0c;还存在一些其他的技术挑战。 一、两个主要问题 &#xff08;1&#xff09;梯度消失和梯度爆炸问题 这是RNN中最显著的问题之…

JMU 数科 数据库与数据仓库期末总结(4)实验设计题

E-R图 实体-关系图 E-R图的组成要素主要包括&#xff1a; 实体&#xff08;Entity&#xff09;&#xff1a;实体代表现实世界中可相互区别的对象或事物&#xff0c;如顾客、订单、产品等。在图中&#xff0c;实体通常用矩形表示&#xff0c;并在矩形内标注实体的名称。 属性…

大话设计模式解读03-装饰模式

本篇文章&#xff0c;来解读《大话设计模式》的第6章——装饰模式。并通过C代码实现实例代码的功能。 注&#xff1a;第3~6章讲的是设计模式中的一些原则&#xff08;第3章&#xff1a;单一职责原则&#xff1b;第4章&#xff1a;开放-封闭原则&#xff1b;第5章&#xff1a;依…

C#知识|模块化分层学习笔记

哈喽&#xff0c;你好&#xff0c;我是雷工&#xff01; 01 基本分层 典型的两层结构&#xff1a;由UI层 数据访问层 实体类构成。 其中实体类不算一层&#xff0c;本质是一个数据载体。 02 模块化分层 模块概念&#xff1a;在.NET平台中&#xff0c;模块主要是指类库项目。…

2024.6.17 作业 xyt

今日作业&#xff1a; 升级优化自己应用程序的登录界面。 要求&#xff1a; 1. qss实现 2. 需要有图层的叠加 &#xff08;QFrame&#xff09; 3. 设置纯净窗口后&#xff0c;有关闭等窗口功能。 4. 如果账号密码正确…

Modbus协议转Profibus协议模块接热传感器配置攻略

一、前言 在工业自动化控制领域&#xff0c;Modbus协议和Profibus协议是两种常见的通讯协议&#xff0c;它们在设备之间传输数据起着至关重要的作用。而Modbus协议转Profibus协议模块&#xff08;XD-MDPB100&#xff09;设备&#xff0c;则扮演着连接不同通讯协议的桥梁角色。…

新质生产力水平测算与中国经济增长新动能(dta数据及do代码)

时间跨度&#xff1a;2012-2022年 数据范围&#xff1a;全国30个省份&#xff08;不含港澳台、西藏&#xff09; 数据指标&#xff1a; 参考韩文龙等的做法&#xff0c;收集了全部控制变量与稳定性检验所需变量。 类型 符号 变量 变量定义 被解释变量 GDP 各省人均GDP…

Linux 并发与竞争基础知识学习

Linux 并发与竞争 并发与竞争 Linux 系统是个多任务操作系统&#xff0c;会存在多个任务同时访问同一片内存区域&#xff0c;这些任务可能会相互覆盖这段内存中的数据&#xff0c;造成内存数据混乱。针对这个问题必须要做处理&#xff0c;严重的话可能会导致系统崩溃。现在的…

Swift开发——存储属性与计算属性

Swift语言开发者建议程序设计者多用结构体开发应用程序。在Swift语言中,结构体具有了很多类的特性(除类的与继承相关的特性外),具有属性和方法,且为值类型。所谓的属性是指结构体中的变量或常量,所谓的方法是指结构体中的函数。在结构体中使用属性和方法是因为:①匹别于结…

[ARM-2D 专题]3. ##运算符

C语言的宏系统相当强大&#xff0c;它允许使用##符号来处理预处理期的文本替换。这种用法被称为标记连接&#xff08;token pasting&#xff09;操作&#xff0c;其结果是将两个标记紧紧地连接在一起&#xff0c;而省略掉它们之间的所有空格。在复杂的宏定义中&#xff0c;运用…

数组元素的内存地址计算【数据结构与算法C#版】

数组元素被存储在连续的内存空间中&#xff0c;这意味着计算数组元素的内存地址非常容易。给定数组内存地址&#xff08;首 元素内存地址&#xff09;和某个元素的索引&#xff0c;我们可以使用下方图 所示的公式计算得到该元素的内存地址&#xff0c;从而直接 访问该元素。 观…

探索CSS clip-path: polygon():塑造元素的无限可能

在CSS的世界里&#xff0c;clip-path 属性赋予了开发者前所未有的能力&#xff0c;让他们能够以非传统的方式裁剪页面元素&#xff0c;创造出独特的视觉效果。其中&#xff0c;polygon() 函数尤其强大&#xff0c;它允许你使用多边形来定义裁剪区域的形状&#xff0c;从而实现各…

【C语言】排序算法 -------- 计数排序

个人主页 创作不易&#xff0c;感谢大家的关注&#xff01; 文章目录 1. 计数排序的概念2. 计数排序使用场景3. 计数排序思想4. 计数排序实现过程5. 计数排序的效率6. 总结&#xff08;附源代码&#xff09; 1. 计数排序的概念 计数排序是一种非比较的排序算法&#xff0c;其…

二、交换机介绍及vlan原理

目录 一、交换机 1.1、交换机处理数据帧的三种行为 1.2、初始化通信 二、虚拟局域网&#xff08;VLAN&#xff09; 三、vlan间通信 3.1、子接口 3.2、三层交换机 一、交换机 交换机&#xff1a;隔离冲突域&#xff0c;交换机每个接口都有一个网卡&#…

解放代码:识别与消除循环依赖的实战指南

目录 一、对循环依赖的基本认识 &#xff08;一&#xff09;代码中形成循环依赖的说明 &#xff08;二&#xff09;无环依赖的原则 二、识别和消除循环依赖的方法 &#xff08;一&#xff09;使用JDepend识别循环依赖 使用 Maven 集成 JDepend 分析报告识别循环依赖 &a…

超越中心化:Web3如何塑造未来数字生态

随着技术的不断发展&#xff0c;人们对于网络和数字生态的期望也在不断提升。传统的中心化互联网模式虽然带来了便利&#xff0c;但也暴露出了诸多问题&#xff0c;比如数据滥用、信息泄露、权力集中等。在这样的背景下&#xff0c;Web3技术应运而生&#xff0c;旨在打破传统中…

Shopee API接口:获取搜索栏生成的商品结果列表

一、平台介绍 Shopee&#xff0c;作为东南亚领先的电商平台&#xff0c;一直致力于为卖家和买家提供便捷、高效的在线购物体验。为了满足广大开发者的需求&#xff0c;Shopee提供了丰富的API接口服务&#xff0c;帮助卖家和第三方开发者更好地与平台进行数据交互&#xff0c;实…

ucos抢占式实时多任务操作系统 (RTOS)。

介绍 uCOS (也称为 μC/OS 或 Micro-Controller Operating System) 是一个开源的、可移植的、可裁剪的、抢占式实时多任务操作系统 (RTOS)。它最初由 Jean J. Labrosse 编写&#xff0c;并广泛用于嵌入式系统设计中。uCOS 是一个小型的 RTOS&#xff0c;非常适合那些需要实时性…

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-ABKDE卷积神经网络自适应…

基于深度学习网络的USB摄像头实时视频采集与人脸检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将摄像头对这播放视频的显示器&#xff0c;然后进行识别&#xff0c;识别结果如下&#xff1a; 本课题中&#xff0c;使用的USB摄像头为&#xff…