代理模式:控制对象访问的守门员[特殊字符],优雅实现功能增强与访问控制!

发布于:2025-06-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

代理模式:控制对象访问的守门员🔐,优雅实现功能增强与访问控制!



前言:为什么需要代理?🤔

各位宝子们,今天我们来聊一个设计模式界的"守门员"——代理模式!😎 还在为如何控制对象访问而头疼吗?还在为如何在不修改原有代码的情况下增加功能而烦恼吗?代理模式来拯救你啦!

代理模式是设计模式家族中的"访问控制专家",它能帮我们优雅地控制对对象的访问,同时还能在不修改原有代码的情况下增加新功能。今天就带大家彻底搞懂这个"看似简单,实则强大"的设计模式!💯


一、代理模式:访问控制的专家 🛡️

1.1 什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对其他对象的访问。就像现实生活中的经纪人、律师一样,代理对象充当了客户与目标对象之间的中介,客户不直接与目标对象交互,而是通过代理对象间接交互!🤝

1.2 为什么需要代理模式?

想象一下这些场景:

  • 需要控制对敏感对象的访问权限
  • 需要在访问对象时执行额外的操作(如日志记录、性能监控)
  • 需要延迟加载大型资源对象
  • 需要在远程服务器上执行操作
  • 需要为对象添加功能,但不想修改原有代码

这些场景有什么共同点?它们都涉及到对对象访问的控制和增强。代理模式就是为这些场景量身定制的!🚀


二、代理模式的结构:中间人的艺术 🎭

代理模式包含以下几个角色:

  • 抽象主题(Subject):定义了代理对象和真实对象的共同接口,这样就可以在任何使用真实对象的地方使用代理对象
  • 真实主题(Real Subject):定义了代理对象所代表的真实对象,是最终要引用的对象
  • 代理(Proxy):保存一个引用使得代理可以访问实体,并提供一个与Subject接口相同的接口,这样代理就可以用来替代实体
// 抽象主题
public interface Subject {
    void request();
}

// 真实主题
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题处理请求");
    }
}

// 代理
public class Proxy implements Subject {
    private RealSubject realSubject;
    
    public Proxy() {
        this.realSubject = new RealSubject();
    }
    
    @Override
    public void request() {
        // 前置处理
        preRequest();
        
        // 调用真实主题的方法
        realSubject.request();
        
        // 后置处理
        postRequest();
    }
    
    private void preRequest() {
        System.out.println("代理前置处理");
    }
    
    private void postRequest() {
        System.out.println("代理后置处理");
    }
}

// 客户端代码
Subject proxy = new Proxy();
proxy.request();
// 输出:
// 代理前置处理
// 真实主题处理请求
// 代理后置处理

看到了吗?通过代理对象,我们可以在调用真实对象的方法前后添加自己的处理逻辑,而客户端对此一无所知!这就是代理模式的魅力所在!🎩✨


三、代理模式的三种类型:静态代理、动态代理与CGLIB代理 🔄

3.1 静态代理:编译时确定的代理

静态代理是最基础的代理模式,代理类在编译时就已经确定。上面的例子就是一个典型的静态代理。

优点:

  • 实现简单,容易理解
  • 可以在不修改目标对象的前提下扩展目标对象的功能

缺点:

  • 代理类和真实主题类都实现了相同的接口,会产生很多代理类
  • 接口增加方法时,代理类和真实主题类都要维护

3.2 动态代理:运行时生成的代理

Java的动态代理是通过反射机制在运行时动态生成代理类的代理方式。

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("真实主题处理请求");
    }
}

// 动态代理处理器
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("动态代理前置处理: " + method.getName());
        
        // 调用真实主题的方法
        Object result = method.invoke(target, args);
        
        // 后置处理
        System.out.println("动态代理后置处理: " + method.getName());
        
        return result;
    }
}

// 客户端代码
Subject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxyHandler(realSubject);

// 创建动态代理
Subject proxy = (Subject) Proxy.newProxyInstance(
    realSubject.getClass().getClassLoader(),
    realSubject.getClass().getInterfaces(),
    handler
);

proxy.request();
// 输出:
// 动态代理前置处理: request
// 真实主题处理请求
// 动态代理后置处理: request

优点:

  • 可以代理多个类,只需要一个代理处理器
  • 可以在运行时动态地创建代理,无需手动编写代理类

缺点:

  • 只能代理实现了接口的类
  • 反射调用方法比直接调用方法性能差

3.3 CGLIB代理:基于继承的代理

CGLIB(Code Generation Library)是一个强大的高性能代码生成库,可以在运行时扩展Java类并实现接口。CGLIB通过生成目标类的子类来实现代理。

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

import java.lang.reflect.Method;

// 真实主题(不需要实现接口)
public class RealSubject {
    public void request() {
        System.out.println("真实主题处理请求");
    }
}

// CGLIB代理拦截器
public class CglibProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置处理
        System.out.println("CGLIB代理前置处理: " + method.getName());
        
        // 调用真实主题的方法
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置处理
        System.out.println("CGLIB代理后置处理: " + method.getName());
        
        return result;
    }
}

// 客户端代码
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new CglibProxyInterceptor());

// 创建CGLIB代理
RealSubject proxy = (RealSubject) enhancer.create();

proxy.request();
// 输出:
// CGLIB代理前置处理: request
// 真实主题处理请求
// CGLIB代理后置处理: request

优点:

  • 可以代理没有实现接口的类
  • 性能比JDK动态代理更好

缺点:

  • 不能代理final类和final方法
  • 需要引入第三方库

四、代理模式实战:实际应用案例 💼

4.1 图片延迟加载

想象一个图片查看器应用,加载高清图片可能需要很长时间。我们可以使用代理模式来实现图片的延迟加载,只有当真正需要显示图片时才加载图片数据。

// 图片接口
public interface Image {
    void display();
}

// 真实图片
public class RealImage implements Image {
    private String filename;
    
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }
    
    private void loadFromDisk() {
        System.out.println("加载图片: " + filename);
    }
    
    @Override
    public void display() {
        System.out.println("显示图片: " + filename);
    }
}

// 代理图片
public class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;
    
    public ProxyImage(String filename) {
        this.filename = filename;
    }
    
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 客户端代码
Image image = new ProxyImage("高清图片.jpg");

// 图片未加载
System.out.println("图片未加载");

// 图片加载并显示
image.display();

// 再次显示图片(不会重新加载)
image.display();

这个例子展示了代理模式如何实现延迟加载,只有在真正需要时才创建昂贵的对象,提高了系统性能!🚀

4.2 权限控制

代理模式可以用来实现权限控制,只有具有特定权限的用户才能访问某些资源。

// 文档接口
public interface Document {
    void read();
    void write();
}

// 真实文档
public class RealDocument implements Document {
    private String name;
    
    public RealDocument(String name) {
        this.name = name;
    }
    
    @Override
    public void read() {
        System.out.println("读取文档: " + name);
    }
    
    @Override
    public void write() {
        System.out.println("写入文档: " + name);
    }
}

// 权限控制代理
public class ProtectionProxy implements Document {
    private RealDocument realDocument;
    private String userRole;
    
    public ProtectionProxy(String documentName, String userRole) {
        this.realDocument = new RealDocument(documentName);
        this.userRole = userRole;
    }
    
    @Override
    public void read() {
        // 所有用户都可以读取文档
        realDocument.read();
    }
    
    @Override
    public void write() {
        // 只有管理员可以写入文档
        if ("admin".equals(userRole)) {
            realDocument.write();
        } else {
            System.out.println("权限不足,无法写入文档");
        }
    }
}

// 客户端代码
Document adminDocument = new ProtectionProxy("敏感文件.txt", "admin");
adminDocument.read();  // 可以读取
adminDocument.write(); // 可以写入

Document userDocument = new ProtectionProxy("敏感文件.txt", "user");
userDocument.read();  // 可以读取
userDocument.write(); // 权限不足,无法写入

这个例子展示了代理模式如何实现权限控制,保护敏感资源不被未授权的用户访问!🔒

4.3 远程代理

远程代理可以隐藏远程对象的复杂性,使客户端感觉像是在调用本地对象。

// 服务接口
public interface Service {
    String performAction(String data);
}

// 远程服务实现
public class RemoteService implements Service {
    @Override
    public String performAction(String data) {
        return "处理数据: " + data;
    }
}

// 远程代理
public class RemoteProxy implements Service {
    private Service remoteService;
    
    public RemoteProxy() {
        // 在实际应用中,这里会通过网络连接到远程服务
        // 这里简化为直接创建远程服务对象
        this.remoteService = new RemoteService();
    }
    
    @Override
    public String performAction(String data) {
        System.out.println("远程代理: 准备发送数据到远程服务");
        
        // 在实际应用中,这里会通过网络调用远程服务
        String result = remoteService.performAction(data);
        
        System.out.println("远程代理: 接收到远程服务的响应");
        
        return result;
    }
}

// 客户端代码
Service service = new RemoteProxy();
String result = service.performAction("测试数据");
System.out.println("结果: " + result);

这个例子展示了远程代理如何隐藏远程调用的复杂性,使客户端感觉像是在调用本地对象!🌐


五、代理模式在Java标准库中的应用 📚

5.1 Java动态代理

Java的java.lang.reflect.Proxy类提供了创建动态代理的功能,这是Java标准库中代理模式的直接应用。

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

// 创建动态代理的工厂方法
public static <T> T createProxy(T target, Class<?>... interfaces) {
    return (T) Proxy.newProxyInstance(
        target.getClass().getClassLoader(),
        interfaces,
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("调用方法: " + method.getName());
                return method.invoke(target, args);
            }
        }
    );
}

5.2 Spring AOP

Spring的面向切面编程(AOP)就是基于代理模式实现的。Spring AOP使用JDK动态代理或CGLIB来创建目标对象的代理,从而实现方法拦截和横切关注点的模块化。

// Spring AOP配置示例
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public MyAspect myAspect() {
        return new MyAspect();
    }
}

// 切面定义
@Aspect
public class MyAspect {
    @Before("execution(* com.example.service.*.*(..))") // 切点表达式
    public void before(JoinPoint joinPoint) {
        System.out.println("前置通知: " + joinPoint.getSignature().getName());
    }
}

5.3 JDBC连接池

JDBC连接池(如Apache DBCP、HikariCP等)使用代理模式来包装数据库连接,以便在连接关闭时将其返回到池中,而不是真正关闭。

// 简化的JDBC连接池代理示例
public class ConnectionProxy implements Connection {
    private Connection realConnection;
    private ConnectionPool pool;
    
    public ConnectionProxy(Connection realConnection, ConnectionPool pool) {
        this.realConnection = realConnection;
        this.pool = pool;
    }
    
    @Override
    public void close() throws SQLException {
        // 不真正关闭连接,而是将其返回到连接池
        pool.releaseConnection(this);
    }
    
    // 其他Connection方法的代理实现
    // ...
}

六、代理模式的优缺点与适用场景 ⚖️

6.1 优点

  • 职责清晰:真实主题就是实现实际的业务逻辑,不用关心其他非本职责的事务
  • 高扩展性:在不修改目标对象的前提下,可以通过代理对象扩展目标对象的功能
  • 智能化:代理类可以在调用目标方法前后做一些额外工作,如权限控制、日志记录等

6.2 缺点

  • 增加复杂度:引入代理模式会增加系统的复杂度
  • 请求处理速度可能变慢:因为代理对象会对请求进行一些处理,可能会导致请求处理速度变慢
  • 实现复杂:某些代理模式的实现(如动态代理)可能比较复杂

6.3 适用场景

  • 远程代理:为远程对象提供本地代表
  • 虚拟代理:延迟加载大型资源对象
  • 保护代理:控制对敏感对象的访问
  • 智能引用:在访问对象时执行额外操作
  • 缓存代理:为开销大的运算结果提供临时存储

七、代理模式与其他模式的对比 🔄

7.1 代理模式 vs 装饰器模式

  • 代理模式:关注的是控制对对象的访问,可能不会添加新功能
  • 装饰器模式:关注的是动态地给对象添加新功能,不改变其接口

7.2 代理模式 vs 适配器模式

  • 代理模式:提供相同的接口,控制对对象的访问
  • 适配器模式:提供不同的接口,使不兼容的接口可以一起工作

7.3 代理模式 vs 外观模式

  • 代理模式:代理与真实对象实现相同的接口,一般只代理一个对象
  • 外观模式:为子系统提供一个简化的接口,通常涉及多个对象

八、代理模式的最佳实践 🌟

  1. 选择合适的代理类型:根据需求选择静态代理、动态代理或CGLIB代理
  2. 保持接口的一致性:代理对象应该与真实对象实现相同的接口
  3. 注意性能影响:代理可能会影响性能,特别是在频繁调用的场景下
  4. 避免过度代理:不要创建代理的代理的代理…
  5. 考虑线程安全:在多线程环境下,确保代理的线程安全性

总结:代理模式,访问控制的优雅之道 🎯

代理模式是一种非常实用的设计模式,它让我们可以在不修改原有代码的情况下,控制对对象的访问,同时还能增加新功能。它在Java生态系统中应用广泛,从JDK的动态代理到Spring的AOP,再到各种连接池的实现,都能看到代理模式的身影。

在实际开发中,当你需要控制对对象的访问,或者在不修改原有代码的情况下增加新功能时,代理模式是一个非常好的选择!记住,好的设计模式就像好的工具一样,用在对的地方才能发挥最大的作用!🌈

下次当你想要控制对对象的访问时,先问问自己:“我是应该直接访问对象呢,还是应该使用代理模式呢?” 如果你需要在访问对象时执行额外的操作,那么代理模式可能是更好的选择!💪


希望这篇文章对你理解代理模式有所帮助!如果有任何问题,欢迎在评论区留言讨论!👇


网站公告

今日签到

点亮在社区的每一天
去签到