rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 代理模式(Proxy Pattern)全解析

  • 一、代理模式核心概念(通用)
    • 1. 定义
    • 2. 意图
    • 3. 通用核心组件
  • 二、代理模式详细解析(以 “图片延迟加载” 为例)
    • 1. 结构
    • 2. 类图(Mermaid)
    • 3. 时序图(Mermaid)
    • 4. 优点
    • 5. 缺点
  • 三、Java 代码实现(图片延迟加载示例)
    • 1. 抽象主题(Image)
    • 2. 真实主题(RealImage)
    • 3. 代理(ProxyImage)
    • 4. 客户端(Client)
    • 5. 输出结果
  • 四、适用环境
  • 五、模式分析
    • 1. 核心本质
    • 2. 与其他模式的区别
    • 3. 关键设计原则
  • 六、模式扩展
    • 1. 静态代理(Static Proxy)
    • 2. 动态代理(Dynamic Proxy)
    • 3. 常见代理类型及应用
  • 七、模式应用(通用 + Android)
    • 1. 通用领域应用
    • 2. Android 中的应用
      • (1)AIDL 远程代理(远程代理)
      • (2)Glide 图片加载(虚拟代理)
      • (3)Retrofit 网络请求(动态代理)
      • (4)ViewPager2 的 Fragment 代理(保护代理)
  • 八、总结
  • Cglib代理
  • 代理模式与装饰者模式的区别

代理模式(Proxy Pattern)全解析

代理模式是结构型设计模式的核心成员,其核心目标是为其他对象提供一种代理(Proxy)以控制对这个对象的访问。它如同现实中的 “中介”—— 租客(客户端)不直接与房东(真实对象)打交道,而是通过中介(代理)完成看房、签约等操作,中介可在过程中添加筛选租客、审核资质等控制逻辑。

23种设计模式--代理模式

一、代理模式核心概念(通用)

1. 定义

为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可通过代理对象对目标对象的访问进行控制或附加额外操作(如权限校验、日志记录、延迟加载等)。

2. 意图

  • 控制访问:限制客户端对目标对象的直接访问,如权限校验(仅管理员可操作)。
  • 附加功能:在访问目标对象前后添加额外逻辑,如日志记录、缓存、事务管理等。
  • 隔离目标对象:客户端无需直接依赖目标对象,降低耦合(如远程代理隐藏网络通信细节)。
  • 延迟初始化:延迟创建开销大的目标对象,仅在真正需要时初始化(如大图片加载前用占位图代替)。

3. 通用核心组件

代理模式的核心是 “代理类” 与 “目标类” 实现相同接口,通过代理类间接访问目标类,包含 3 个核心角色:

角色名称职责描述
Subject(抽象主题)定义代理类和目标类的共同接口(或抽象类),声明核心业务方法,确保代理与目标对象可被客户端透明使用。
RealSubject(真实主题)实现抽象主题,是 “被代理的目标对象”,提供核心业务逻辑的具体实现(如实际加载图片、执行数据库操作)。
Proxy(代理)实现抽象主题,持有一个 RealSubject 类型的引用(指向目标对象)。代理类不直接实现核心业务逻辑,而是在调用目标对象的方法前后,添加控制逻辑(如权限检查、日志),或直接替代目标对象完成部分操作(如延迟加载)。

二、代理模式详细解析(以 “图片延迟加载” 为例)

以 “图片加载” 为场景:大型图片(如 4K 分辨率)加载耗时且耗内存,若页面初始化时立即加载所有图片,会导致卡顿。代理模式通过 “图片代理” 实现延迟加载 —— 页面初始化时,代理先显示占位图;当用户滚动到图片位置时,代理再调用真实图片加载逻辑,提升用户体验。

1. 结构

  1. Subject(抽象主题):Image,定义display()方法(显示图片)。
  2. RealSubject(真实主题):RealImage,实现display(),负责实际加载并显示图片(耗时操作)。
  3. Proxy(代理):ProxyImage,实现Image,持有RealImage引用。display()方法中,先判断RealImage是否初始化:未初始化则创建并加载图片,再显示;已初始化则直接显示。

2. 类图(Mermaid)

classDiagram
    %% 抽象主题:图片接口
    class Image {
        <<Interface>>
        +display(): void  // 显示图片
    }

    %% 真实主题:实际图片(被代理对象)
    class RealImage {
        -filename: String  // 图片路径
        +RealImage(filename: String)
        +loadImage(): void  // 实际加载图片(耗时操作)
        +display(): void  // 显示图片
    }

    %% 代理:图片代理(控制访问)
    class ProxyImage {
        -filename: String  // 图片路径
        -realImage: RealImage  // 持有真实图片引用(延迟初始化)
        +ProxyImage(filename: String)
        +display(): void  // 控制显示:需要时才加载真实图片
    }

    %% 客户端:使用图片的场景
    class Client {
        +viewImage(): void
    }

    %% 关系梳理
    %%真实主题实现抽象主题
    Image <|-- RealImage
    %%代理实现抽象主题(接口一致)
    Image <|-- ProxyImage
    ProxyImage o-- RealImage : 持有(核心:代理关联真实对象)
    Client --> Image : 依赖抽象主题(透明使用代理或真实对象)

3. 时序图(Mermaid)

以 “客户端首次显示图片(触发延迟加载)” 为例,展示代理模式的调用流程:

sequenceDiagram
    participant Client(客户端)
    participant ProxyImage(图片代理)
    participant RealImage(真实图片)

    %% 1. 客户端创建图片代理(不立即加载真实图片)
    Client->>ProxyImage: new ProxyImage("large_image.jpg")
    ProxyImage-->>Client: 返回ProxyImage实例(realImage为null)

    %% 2. 客户端请求显示图片
    Client->>ProxyImage: display()(显示图片)

    %% 3. 代理检查真实图片是否初始化
    ProxyImage->>ProxyImage: 检查realImage是否为null(是)

    %% 4. 代理创建并加载真实图片(延迟初始化)
    ProxyImage->>RealImage: new RealImage("large_image.jpg")
    RealImage-->>ProxyImage: 返回RealImage实例
    ProxyImage->>RealImage: loadImage()(实际加载,耗时)
    RealImage-->>ProxyImage: 图片加载完成

    %% 5. 代理调用真实图片的display()
    ProxyImage->>RealImage: display()(显示图片)
    RealImage-->>ProxyImage: 图片显示完成

    %% 6. 代理返回结果给客户端
    ProxyImage-->>Client: 图片显示完成

    %% 7. 客户端再次请求显示(无需重复加载)
    Client->>ProxyImage: display()
    ProxyImage->>ProxyImage: 检查realImage是否为null(否)
    ProxyImage->>RealImage: display()
    RealImage-->>ProxyImage: 图片显示完成
    ProxyImage-->>Client: 图片显示完成

4. 优点

  • 控制访问灵活:可在访问目标对象前添加权限校验、限流等控制逻辑(如保护代理)。
  • 功能扩展无侵入:在不修改目标对象代码的前提下,通过代理添加日志、缓存等功能(符合 “开闭原则”)。
  • 延迟初始化:对创建成本高的对象(如大图片、数据库连接),通过代理延迟初始化,减少系统启动时间。
  • 隔离远程对象:远程代理隐藏网络通信细节(如 RPC 框架),客户端无需关心远程调用的底层实现。

5. 缺点

  • 增加系统复杂度:需为每个目标对象创建对应的代理类(静态代理),或引入动态代理框架,增加代码量和理解成本。
  • 额外性能开销:代理类的中间层会增加一次方法调用,对性能敏感的场景可能有影响(通常可忽略)。
  • 动态代理局限性:某些语言的动态代理仅支持接口代理(如 Java 的 JDK 动态代理),无法代理类;CGLIB 等类代理可能受 final 类限制。

三、Java 代码实现(图片延迟加载示例)

1. 抽象主题(Image)

定义代理和目标对象的共同接口:

// 抽象主题:图片接口
public interface Image {
    // 显示图片
    void display();
}

2. 真实主题(RealImage)

实现核心业务逻辑(实际加载图片):

// 真实主题:实际图片(被代理对象)
public class RealImage implements Image {
    private final String filename; // 图片路径

    // 构造器:初始化图片路径(不立即加载)
    public RealImage(String filename) {
        this.filename = filename;
    }

    // 实际加载图片(耗时操作,如从网络或本地读取)
    private void loadImage() {
        System.out.println("正在加载图片:" + filename + "(耗时操作)");
        // 模拟加载延迟
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 显示图片(先加载,再显示)
    @Override
    public void display() {
        loadImage(); // 加载图片
        System.out.println("显示图片:" + filename);
    }
}

3. 代理(ProxyImage)

控制访问,实现延迟加载:

// 代理:图片代理(控制访问,延迟加载)
public class ProxyImage implements Image {
    private final String filename; // 图片路径
    private RealImage realImage; // 持有真实图片引用(延迟初始化)

    // 构造器:仅保存图片路径,不创建RealImage
    public ProxyImage(String filename) {
        this.filename = filename;
    }

    // 显示图片:需要时才创建并加载真实图片
    @Override
    public void display() {
        // 延迟初始化:首次调用时才创建RealImage
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        // 调用真实图片的显示方法
        realImage.display();
    }
}

4. 客户端(Client)

通过代理透明访问目标对象:

// 客户端:使用图片代理
public class Client {
    public static void main(String[] args) {
        System.out.println("=== 页面初始化 ===");
        // 创建代理(不加载真实图片,仅保存路径)
        Image image1 = new ProxyImage("4k_nature.jpg");
        Image image2 = new ProxyImage("4k_city.jpg");

        System.out.println("\n=== 用户滚动到第一张图片 ===");
        image1.display(); // 首次显示:触发加载和显示

        System.out.println("\n=== 用户滚动到第二张图片 ===");
        image2.display(); // 首次显示:触发加载和显示

        System.out.println("\n=== 用户再次滚动到第一张图片 ===");
        image1.display(); // 再次显示:直接使用已加载的图片(无重复加载)
    }
}

5. 输出结果

=== 页面初始化 ===

=== 用户滚动到第一张图片 ===
正在加载图片:4k_nature.jpg(耗时操作)
显示图片:4k_nature.jpg

=== 用户滚动到第二张图片 ===
正在加载图片:4k_city.jpg(耗时操作)
显示图片:4k_city.jpg

=== 用户再次滚动到第一张图片 ===
正在加载图片:4k_nature.jpg(耗时操作)
显示图片:4k_nature.jpg

四、适用环境

代理模式适用于以下场景,核心判断标准是 “需控制对目标对象的访问,或需在访问前后附加逻辑”:

  1. 控制访问权限:需限制客户端对目标对象的访问(如保护代理,仅管理员可调用敏感方法)。
  2. 延迟初始化:目标对象创建成本高(如大文件、数据库连接),需延迟到真正使用时创建(虚拟代理)。
  3. 远程访问:目标对象位于远程服务器,需通过代理处理网络通信(如 RPC 框架的远程代理)。
  4. 附加操作:需在访问目标对象前后添加日志、缓存、事务等功能(如日志代理、缓存代理)。
  5. 屏蔽复杂实现:目标对象的实现复杂(如加密、序列化),需通过代理简化客户端使用(如外观代理)。

五、模式分析

1. 核心本质

代理模式的本质是 “控制访问 + 接口一致 + 间接交互”:

  • 控制访问:代理作为中间层,决定何时、如何、是否允许访问目标对象(如权限校验、延迟加载)。
  • 接口一致:代理与目标对象实现相同接口(Image),确保客户端可透明使用(无需区分代理和目标对象)。
  • 间接交互:客户端始终通过代理访问目标对象,目标对象的变化(如实现修改)不影响客户端。

2. 与其他模式的区别

模式核心差异典型场景
代理模式控制对目标对象的访问,附加逻辑通常是辅助性的(如日志、权限)延迟加载、远程调用、权限控制
装饰模式动态扩展目标对象的功能,核心是 “增强” 而非 “控制”咖啡加调料、IO 流缓冲
适配器模式转换接口(使不兼容的接口可协同工作),不控制访问旧系统接口适配新系统
门面模式为多个子系统提供统一入口,简化访问,不针对单个对象智能家居控制、第三方 SDK 封装

3. 关键设计原则

  • 接口一致性:代理必须与目标对象实现相同接口(遵循 “里氏替换原则”),确保客户端透明使用。
  • 单一职责:代理应专注于控制访问或附加特定功能(如日志代理只负责日志,权限代理只负责权限),避免 “万能代理”。
  • 最小知识原则:客户端只需了解代理,无需了解目标对象的实现细节,降低耦合。

六、模式扩展

代理模式根据控制逻辑和应用场景,可分为多种变体:

1. 静态代理(Static Proxy)

  • 特点:代理类在编译期手动编写,与目标类一一对应(如ProxyImage对应RealImage)。
  • 优点:简单直观,无需依赖框架。
  • 缺点:目标类增多时,代理类数量爆炸;目标接口修改时,代理类需同步修改。
  • 适用场景:目标类少且接口稳定的简单场景。

2. 动态代理(Dynamic Proxy)

  • 特点:代理类在运行时动态生成(无需手动编写),通过反射或字节码技术实现(如 Java 的 JDK 动态代理、CGLIB)。

  • 优点:一个动态代理类可代理多个目标类(实现相同接口),减少代码量。

  • 缺点:实现复杂,部分场景有性能开销;JDK 动态代理仅支持接口代理。

    效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法。

    应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类创建代理类。

    在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。

    InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。

    注意:接口数不能超过65535,JDK代码里进行了判断

  • 适用场景:框架开发(如 Spring AOP、Retrofit),需批量代理多个类的场景。

Java JDK 动态代理示例:

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

// 动态代理处理器(通用日志代理)
class LogInvocationHandler implements InvocationHandler {
    private final Object target; // 目标对象

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

    // 动态生成代理方法:调用目标方法前后添加日志
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志:调用方法 " + method.getName() + " 前");
        Object result = method.invoke(target, args); // 调用目标方法
        System.out.println("日志:调用方法 " + method.getName() + " 后");
        return result;
    }
}

// 客户端使用动态代理
public class DynamicProxyClient {
    public static void main(String[] args) {
        // 目标对象
        Image realImage = new RealImage("dynamic_proxy.jpg");
        // 创建动态代理(实现Image接口)
        Image proxyImage = (Image) Proxy.newProxyInstance(
            Image.class.getClassLoader(),
            new Class[]{Image.class},
            new LogInvocationHandler(realImage)
        );
        // 通过代理调用,自动添加日志
        proxyImage.display();
    }
}

底层实现

Proxy类里 proxyGenerator

$Proxy+num(CAS原子操作)

>底层实现

3. 常见代理类型及应用

代理类型核心功能典型场景
虚拟代理(Virtual Proxy)延迟初始化开销大的对象大图片加载、大型数据集加载。
允许内存开销较大的对象在需要的时候创建。只有我们真正需要这个对象的时候才创建。
远程代理(Remote Proxy)隐藏远程对象的网络通信细节RPC 框架(如 Dubbo)、AIDL。
可以隐藏一个对象存在于不同地址空间的事实。也使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
保护代理(Protection Proxy)控制目标对象的访问权限敏感操作的权限校验(如管理员接口)。
为不同的客户提供不同级别的目标对象访问权限。
缓存代理(Cache Proxy)缓存目标对象的返回结果数据库查询缓存、API 接口缓存。
为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。
日志代理(Logging Proxy)记录目标对象的访问日志接口调用日志、审计日志。
记录目标对象的访问日志。接口调用日志、审计日志。
智能引用代理(Smart Reference Proxy)管理目标对象的生命周期(如计数引用)资源自动释放(文件、连接)。
当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
防火墙代理(Firewall Proxy)控制网络资源的访问,保护主题免于恶意客户的侵害。
同步代理(SynchronizationProxy)在多线程的情况下为主题提供安全的访问。
写入时复制代理(Copy-On-Write Proxy)用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。是虚拟代理的一个变体。
复杂隐藏代理(Complexity HidingProxy)用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Façade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。

七、模式应用(通用 + Android)

1. 通用领域应用

  • Spring AOP:通过动态代理(JDK 或 CGLIB)实现切面编程,在目标方法前后添加日志、事务、缓存等功能(如@Transactional注解通过代理实现事务控制)。
  • Retrofit:使用动态代理为接口生成实现类,将接口方法转换为 HTTP 请求(代理处理 URL 拼接、参数解析、网络请求等逻辑)。
  • MyBatis:通过动态代理为 Mapper 接口生成实现类,代理类将接口方法转换为 SQL 执行逻辑。
  • RPC 框架(Dubbo、gRPC):远程代理隐藏跨进程通信细节,客户端调用本地代理如同调用本地方法,代理自动完成网络传输。

2. Android 中的应用

Android 框架和常用库大量使用代理模式,核心场景集中在远程通信、延迟加载和功能增强:

(1)AIDL 远程代理(远程代理)

  • 背景:Android 中不同进程的组件通信需通过 AIDL(Android 接口定义语言),跨进程调用涉及 Binder 机制,逻辑复杂。

  • 代理角色:AIDL 会自动生成代理类(Stub和Proxy),客户端通过Proxy类调用远程服务,Proxy隐藏 Binder 通信细节(如数据序列化、跨进程传输)。

  • 核心逻辑:

    // AIDL接口(ITestService.aidl)
    interface ITestService {
        void doSomething();
    }
    
    // 自动生成的Proxy类(简化版)
    public static class Proxy implements ITestService {
        private final IBinder mRemote;
    
        public Proxy(IBinder remote) {
            mRemote = remote;
        }
    
        @Override
        public void doSomething() {
            // 代理处理跨进程通信:封装数据、发送请求
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_doSomething, data, reply, 0);
                reply.readException();
            } finally {
                data.recycle();
                reply.recycle();
            }
        }
    }
    
    // 客户端通过Proxy调用远程服务
    ITestService service = ITestService.Stub.asInterface(binder); // 获取Proxy
    service.doSomething(); // 代理自动处理跨进程通信
    

(2)Glide 图片加载(虚拟代理)

  • 背景:Glide 是 Android 常用图片加载库,需处理网络请求、图片解码、内存缓存等复杂逻辑,且需支持延迟加载和占位图。

  • 代理角色:Glide 的RequestBuilder作为代理,客户端调用load(url).into(imageView)时,代理先显示占位图,后台异步加载图片,加载完成后替换为真实图片(隐藏复杂加载逻辑)。

  • 核心逻辑:

    // 客户端通过Glide代理加载图片
    Glide.with(context)
         .load("https://example.com/large-image.jpg") // 代理保存图片地址
         .placeholder(R.drawable.placeholder) // 显示占位图(代理逻辑)
         .into(imageView); // 代理触发异步加载,完成后显示真实图片
    

(3)Retrofit 网络请求(动态代理)

  • 背景:Retrofit 简化 HTTP 请求,通过接口定义 API,无需手动编写网络请求代码。

  • 代理角色:Retrofit 使用动态代理为 API 接口生成实现类,代理类将接口方法(如@GET("users"))转换为 OkHttp 请求,处理 URL 拼接、参数注入、响应解析等逻辑。

  • 核心逻辑:

    // 定义API接口
    public interface ApiService {
        @GET("users")
        Call<List<User>> getUsers();
    }
    
    // Retrofit生成动态代理
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
    
    ApiService service = retrofit.create(ApiService.class); // 动态代理生成实现类
    service.getUsers().enqueue(...); // 代理处理HTTP请求细节
    

(4)ViewPager2 的 Fragment 代理(保护代理)

  • 背景:ViewPager2 管理 Fragment 生命周期时,需控制 Fragment 的创建、销毁时机,避免频繁重建。
  • 代理角色:ViewPager2 的FragmentStateAdapter作为代理,控制 Fragment 的实例化(createFragment())和销毁(destroyItem()),仅在需要显示时创建 Fragment,优化内存和性能。

八、总结

代理模式是 “控制对象访问” 的核心解决方案,其核心价值在于 “灵活控制 + 无侵入扩展 + 透明交互”:

  1. 核心优势:可在不修改目标对象的前提下控制访问或添加功能,降低客户端与目标对象的耦合,支持延迟加载和远程访问,符合 “开闭原则”。
  2. 适用场景:需权限控制、延迟初始化、远程通信、日志缓存等场景(如 Spring AOP、Retrofit、Glide)。
  3. 实践建议:
    • 简单场景用静态代理,复杂场景(多目标类)用动态代理;
    • 代理应专注于单一职责(如仅日志或仅权限),避免过度复杂;
    • 优先通过接口定义抽象主题,确保代理与目标对象的一致性;
    • 动态代理需注意性能开销(尤其是高频调用场景)。

代理模式不仅是一种设计技巧,更是一种 “间接控制” 的思维 —— 通过引入中间层(代理),隔离客户端与目标对象,在不破坏原有逻辑的前提下实现灵活控制和功能扩展,是框架设计和系统解耦的重要工具。

Cglib代理

上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标对象子类的方式实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类.

代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

代码示例:

目标对象类:UserDao.java

/**
 * 目标对象,没有实现任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

Cglib代理工厂:ProxyFactory.java

/**
 * Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
     //维护目标对象
    private Object target;

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

   //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
       //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");
        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        System.out.println("提交事务...");
        return returnValue;
    }
}

测试类:

/**
 * 测试类
 */
public class App {

    @Test
    public void test(){
        //目标对象
        UserDao target = new UserDao();

        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法
        proxy.save();
    }
}

AOP(AspectOrientedProgramming):

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码---解耦。

**

在Spring的AOP编程中:

如果加入容器的目标对象有实现接口,用JDK代理

如果目标对象没有实现接口,用Cglib代理**

代理模式与装饰者模式的区别

UML类图基本没区别,都是实现同一个接口,一个类包装另一 个类。 两者的定义:

装饰器模式:能动态的新增或组合对象的行为

在不改变接口的前提下,动态扩展对象的功能

代理模式:为其他对象提供一种代理以控制对这个对象的访问

在不改变接口的前提下,控制对象的访问

装饰模式是“新增行为”,而代理模式是“控制访问”。关键就是我们如何判断是“新增行 为”还是“控制访问”。你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大 家就知道是在限制。

让别人帮助你做你并不关心的事情,叫代理模式

为让自己的能力增强,使得增强后的自己能够使用更多的方法,拓展在自己基础之上的功能的,叫装饰器模式

对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。他们之间的边界确实比较模糊,两者都是对类的方法进行扩展,具体区别如下:

1、装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。

2、装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;

3、装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;

最近更新:: 2025/10/22 15:36
Contributors: luokaiwen, 罗凯文