Bootstrap

【Java】设计模式——代理模式

引言

代理模式(Proxy Pattern)是结构型设计模式之一,旨在为其他对象提供一种代理以控制对该对象的访问。它通过为对象创建一个替代对象或代表对象,在客户端与目标对象之间插入一个中介层,从而达到某些功能,比如延迟加载、权限控制、日志记录等。

Java 中的代理模式分为两种:静态代理和动态代理。在本文中,我们将详细讨论代理模式的概念、实现方式及应用场景。


1. 代理模式概述

代理模式是指通过代理对象间接访问目标对象。代理对象在客户端和目标对象之间充当中介角色,可以对目标对象进行一些额外的处理,比如日志记录、安全控制、事务管理、懒加载等。

1.1 代理模式的组成部分

  • 主题(Subject):目标对象或者接口,通常是被代理的对象。
  • 代理对象(Proxy):负责控制对主题对象的访问,代理对象通常会调用目标对象的相应方法,可以在调用前后加入一些额外的逻辑。
  • 客户端(Client):请求代理对象而不是直接请求主题对象。

2. 代理模式的分类

2.1 静态代理

静态代理是在编译时就确定代理类和目标类的关系,代理类和目标类都实现相同的接口,代理类通过持有目标类的实例来实现方法的转发。

静态代理的优点:

  • 对目标对象的调用透明,不需要修改目标对象的代码。
  • 可以在代理类中添加一些额外的功能,比如日志、安全控制等。

静态代理的缺点:

  • 每增加一个目标类,就需要为其创建一个代理类,代理类的数量会随着目标类的增加而增多,导致代码冗余。

静态代理示例:

// 目标接口
public interface Subject {
    void request();
}

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

// 代理类
public class Proxy implements Subject {
    private RealSubject realSubject;

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

    @Override
    public void request() {
        // 在代理类中添加一些额外的功能
        System.out.println("Proxy: Logging before request");
        realSubject.request();
        System.out.println("Proxy: Logging after request");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

输出:

Proxy: Logging before request
RealSubject: Handling request
Proxy: Logging after request

2.2 动态代理

动态代理是在运行时生成代理对象,而不是在编译时生成。这通常使用 Java 的反射机制,通过 Proxy 类动态生成代理对象。

Java 的 java.lang.reflect.Proxy 类和 InvocationHandler 接口提供了动态代理的实现。动态代理适用于接口类型的目标对象,而不需要为每个目标对象创建代理类,具有更好的灵活性。

动态代理的优点:

  • 不需要为每个目标类编写代理类,减少了代码冗余。
  • 可以在运行时动态生成代理对象,灵活性较高。

动态代理的缺点:

  • 需要依赖反射机制,性能稍差。
  • 仅支持接口类型的目标对象。

动态代理示例:

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

// 目标接口
public interface Subject {
    void request();
}

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

// 动态代理处理器
public 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("Proxy: Logging before request");
        Object result = method.invoke(target, args);
        System.out.println("Proxy: Logging after request");
        return result;
    }
}

// 客户端
public class Client {
    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.request();
    }
}

输出:

Proxy: Logging before request
RealSubject: Handling request
Proxy: Logging after request

3.代理模式的应用场景

代理模式广泛应用于以下场景:

1.远程代理:当客户端需要访问远程服务器上的对象时,可以使用代理模式来隐藏远程对象的复杂性。例如,RMI(远程方法调用)机制中就是通过代理模式来实现远程方法调用的。

2.虚拟代理:用于延迟加载对象的创建。例如,某些大型对象的加载可能非常昂贵,因此可以通过虚拟代理来推迟对象的创建,直到实际需要时再进行加载。

3.保护代理:用于控制对某些对象的访问,通常用于实现权限控制。例如,在多用户系统中,可以通过保护代理来确保不同角色的用户访问不同级别的数据。

4.缓存代理:通过代理来缓存目标对象的计算结果,避免重复计算。例如,在计算密集型的操作中,可以通过缓存代理来提高性能。

5.智能代理:除了控制访问外,智能代理还可以在调用目标对象方法时执行一些额外的操作,如统计调用次数、管理资源等。


4. 代理模式的优缺点

优点:

  • 透明性:代理对象和真实对象通常实现相同的接口,因此客户端无需了解代理对象的存在。
  • 附加功能:在代理对象中可以添加额外的功能,如权限检查、日志记录等。
  • 降低耦合度:代理模式使得客户端和目标对象之间的关系变得更加松耦合,方便扩展和维护。

缺点:

  • 性能开销:代理模式引入了额外的代理对象,会增加方法调用的时间消耗,尤其是使用动态代理时,反射会带来一定的性能开销。
  • 代码复杂度:在某些场景下,代理模式可能导致系统中出现大量的代理类,增加了代码的复杂性。
;