聊一聊:设计模式——代理模式

110 阅读4分钟

你好,我是风一样的树懒,一个工作十多年的后端专家,曾就职京东、阿里等多家互联网头部企业。公众号“吴计可师”,已经更新了近百篇高质量的面试相关文章,喜欢的朋友欢迎关注点赞

代理模式详解


一、定义与核心思想

代理模式(Proxy Pattern)是一种结构型设计模式,通过创建代理对象控制对原始对象的访问。代理充当客户端与真实对象之间的中介,用于延迟处理、访问控制、功能增强等场景,核心在于间接访问职责分离


二、适用场景

  1. 远程代理:访问远程对象(如RPC调用)。
  2. 虚拟代理:延迟加载大资源(如图片懒加载)。
  3. 保护代理:控制权限(如接口访问控制)。
  4. 缓存代理:缓存请求结果,提升性能。
  5. 日志/监控代理:记录方法调用信息。

三、模式结构

角色职责
抽象主题(Subject)定义真实对象与代理的共用接口(如UserService)。
真实主题(Real Subject)实现核心业务逻辑(如UserServiceImpl)。
代理(Proxy)持有真实对象的引用,控制访问并可选增强功能(如UserServiceProxy)。

四、实现方式

1. 静态代理(显式代理)

// 抽象主题
public interface UserService {
    void saveUser(User user);
}

// 真实主题
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser(User user) {
        System.out.println("保存用户:" + user.getName());
    }
}

// 代理类
public class UserServiceProxy implements UserService {
    private UserService realService;
    
    public UserServiceProxy() {
        this.realService = new UserServiceImpl();
    }
    
    @Override
    public void saveUser(User user) {
        checkPermission();  // 增强功能:权限校验
        realService.saveUser(user);
        logOperation();     // 增强功能:日志记录
    }
    
    private void checkPermission() {
        System.out.println("权限校验通过");
    }
    
    private void logOperation() {
        System.out.println("操作日志已记录");
    }
}

// 使用
UserService service = new UserServiceProxy();
service.saveUser(new User("Alice"));

2. 动态代理(运行时生成代理类)

  • JDK动态代理(基于接口)

    public class LoggingHandler implements InvocationHandler {
        private Object target;
        
        public LoggingHandler(Object target) {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("调用方法:" + method.getName());
            return method.invoke(target, args);
        }
    }
    
    // 创建代理
    UserService realService = new UserServiceImpl();
    UserService proxy = (UserService) Proxy.newProxyInstance(
        UserService.class.getClassLoader(),
        new Class[]{UserService.class},
        new LoggingHandler(realService)
    );
    proxy.saveUser(new User("Bob"));
    
  • CGLIB代理(基于类继承)

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserServiceImpl.class);
    enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
        System.out.println("CGLIB代理前置处理");
        return proxy.invokeSuper(obj, args);
    });
    UserService proxy = (UserService) enhancer.create();
    proxy.saveUser(new User("Charlie"));
    

五、核心优势

  1. 职责清晰:代理类专注非业务逻辑(如安全、日志),真实类专注核心功能。
  2. 扩展灵活:动态代理无需为每个类显式创建代理。
  3. 保护真实对象:隐藏敏感操作,控制访问路径。

六、应用案例

  1. Spring AOP:通过动态代理实现事务管理、日志切面。
  2. MyBatis Mapper:接口方法调用代理生成SQL执行。
  3. RPC框架:客户端存根(Stub)代理远程服务调用。
  4. 图片懒加载:虚拟代理延迟加载大图直至需显示时。

七、代理模式 vs 装饰器模式

维度代理模式装饰器模式
目的控制访问,增强非功能性逻辑(权限/日志)增强对象功能,添加新职责
创建时机通常由客户端决定是否使用代理通常由客户端动态组合装饰器
关系代理与目标对象生命周期可能独立装饰器与组件生命周期一致

八、进阶应用

1. 延迟初始化(虚拟代理)

public class ImageProxy implements Image {
    private String path;
    private RealImage realImage;
    
    public ImageProxy(String path) {
        this.path = path;
    }
    
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(path); // 实际使用时加载
        }
        realImage.display();
    }
}

2. 分布式服务代理(远程代理)

// 客户端调用远程服务
public class RemoteServiceProxy implements OrderService {
    @Override
    public Order getOrder(String id) {
        // 网络通信逻辑(如HTTP/RPC)
        return remoteClient.fetchOrder(id);
    }
}

3. 访问控制(保护代理)

public class ProtectedServiceProxy implements AdminService {
    private AdminService realService;
    private User currentUser;
    
    public ProtectedServiceProxy(User user) {
        this.realService = new AdminServiceImpl();
        this.currentUser = user;
    }
    
    @Override
    public void deleteData() {
        if (!currentUser.isAdmin()) {
            throw new SecurityException("权限不足");
        }
        realService.deleteData();
    }
}

九、注意事项

  1. 性能开销:动态代理生成类可能影响启动速度,频繁调用需优化。
  2. 过度使用:简单场景无需引入代理,避免增加复杂度。
  3. 接口限制:JDK动态代理要求目标实现接口,CGLIB不能代理final类/方法。

十、总结

代理模式通过间接访问机制,在不侵入原始代码的前提下,为系统扩展提供强大灵活性。合理运用静态/动态代理技术,可有效解耦核心逻辑与辅助功能,是构建高维护性、高扩展性架构的关键工具。

今天文章就分享到这儿,喜欢的朋友可以关注我的公众号,回复“进群”,可进免费技术交流群。博主不定时回复大家的问题。 公众号:吴计可师