Java 设计模式:代理模式详解

Java 设计模式:代理模式详解

代理模式(Proxy Pattern)是一种结构型设计模式,它通过为目标对象提供一个代理对象来控制对其的访问。代理对象可以在客户端和目标对象之间充当中介,添加额外的功能,如权限控制、延迟加载等。本文将介绍代理模式的定义、实现方式及其在 Java 中的应用。

1. 什么是代理模式?

代理模式的核心思想是:通过代理对象间接访问目标对象,从而在访问前后添加额外逻辑(如日志、权限校验)或控制访问行为(如懒加载)。它适用于需要保护、增强或优化目标对象访问的场景。

模式结构

  • 抽象主题(Subject):定义目标对象和代理对象的公共接口。
  • 真实主题(Real Subject):实现具体业务逻辑,是被代理的对象。
  • 代理对象(Proxy):持有真实主题的引用,控制对其访问并添加额外逻辑。
  • 客户端(Client):通过抽象主题接口调用代理对象。

代理类型

  1. 静态代理:在编译时确定代理关系,代码固定。
  2. 动态代理:运行时通过反射生成代理对象,灵活性高(如 Java 的 Proxy 类)。
  3. CGLIB 代理:基于字节码增强,适用于没有接口的类。

2. 代理模式的实现方式

以下是一个示例:模拟一个文件访问系统,通过代理控制用户对文件的访问权限。

2.1 静态代理实现

定义抽象主题
public interface FileAccess {
    void readFile(String fileName);
}
实现真实主题
public class RealFileAccess implements FileAccess {
    @Override
    public void readFile(String fileName) {
        System.out.println("读取文件: " + fileName);
    }
}
实现代理对象
public class FileAccessProxy implements FileAccess {
    private RealFileAccess realFileAccess;
    private String userRole;

    public FileAccessProxy(String userRole) {
        this.userRole = userRole;
        this.realFileAccess = new RealFileAccess();
    }

    @Override
    public void readFile(String fileName) {
        // 权限校验
        if ("admin".equals(userRole)) {
            realFileAccess.readFile(fileName);
        } else {
            System.out.println("权限不足,无法读取文件: " + fileName);
        }
    }
}
客户端使用
public class Client {
    public static void main(String[] args) {
        // 管理员用户
        FileAccess adminAccess = new FileAccessProxy("admin");
        adminAccess.readFile("data.txt");

        // 普通用户
        FileAccess userAccess = new FileAccessProxy("user");
        userAccess.readFile("data.txt");
    }
}
输出结果
读取文件: data.txt
权限不足,无法读取文件: data.txt

2.2 动态代理实现

Java 提供了 java.lang.reflect.Proxy 类支持动态代理。

定义动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicFileAccessProxy implements InvocationHandler {
    private RealFileAccess realFileAccess;
    private String userRole;

    public DynamicFileAccessProxy(RealFileAccess realFileAccess, String userRole) {
        this.realFileAccess = realFileAccess;
        this.userRole = userRole;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("readFile".equals(method.getName()) && !"admin".equals(userRole)) {
            System.out.println("权限不足,无法读取文件: " + args[0]);
            return null;
        }
        return method.invoke(realFileAccess, args);
    }

    public static FileAccess createProxy(RealFileAccess realFileAccess, String userRole) {
        return (FileAccess) Proxy.newProxyInstance(
                realFileAccess.getClass().getClassLoader(),
                new Class[]{FileAccess.class},
                new DynamicFileAccessProxy(realFileAccess, userRole)
        );
    }
}
客户端使用
public class Client {
    public static void main(String[] args) {
        RealFileAccess realFileAccess = new RealFileAccess();

        // 管理员用户
        FileAccess adminProxy = DynamicFileAccessProxy.createProxy(realFileAccess, "admin");
        adminProxy.readFile("data.txt");

        // 普通用户
        FileAccess userProxy = DynamicFileAccessProxy.createProxy(realFileAccess, "user");
        userProxy.readFile("data.txt");
    }
}
输出结果

同静态代理。


3. 代理模式的优缺点

优点

  1. 访问控制:代理可以添加权限校验、日志等功能,保护目标对象。
  2. 解耦客户端:客户端无需直接与目标对象交互。
  3. 灵活扩展:动态代理支持运行时生成,适应多种场景。

缺点

  1. 复杂性增加:引入代理类可能使系统结构更复杂。
  2. 性能开销:动态代理依赖反射,性能略低于直接调用。
  3. 维护成本:静态代理需为每个目标类编写代理类。

4. 实际应用场景

  • Spring AOP:通过动态代理实现切面功能,如日志、事务管理。
  • 远程调用:RMI 和 Web 服务中的代理,隐藏网络通信细节。
  • 懒加载:如 ORM 框架中延迟加载关联对象。

示例:Spring AOP 代理

@Aspect
@Component
public class LogAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("方法执行前记录日志");
    }
}

Spring 使用动态代理为目标方法添加日志逻辑。


5. 与其他模式的区别

  • 装饰者模式:增强对象功能,保持接口一致;代理模式控制访问,可能不直接暴露目标。
  • 适配器模式:转换接口以兼容;代理模式增强或控制同一接口的行为。
  • 桥接模式:分离抽象与实现;代理模式关注访问控制。

6. 使用代理模式的注意事项

  1. 选择代理类型:静态代理适合简单场景,动态代理适合复杂或通用场景。
  2. 性能优化:动态代理反射开销较大,避免在高频调用中使用。
  3. 接口依赖:动态代理需目标类实现接口,否则需使用 CGLIB。

7. 总结

代理模式通过为目标对象提供中介,实现了访问控制和功能增强的优雅方案。静态代理实现简单,适合固定场景;动态代理灵活通用,广泛应用于框架设计。在 Java 中,代理模式是 Spring、Hibernate 等框架的核心机制之一。掌握这一模式,能有效提升系统的安全性、可维护性和扩展性。

希望这篇博文能帮助你理解代理模式的精髓!如果有其他设计模式相关问题,欢迎留言讨论。

你可能感兴趣的:(设计模式,java,设计模式,代理模式)