目录

代理模式 (Proxy Pattern)

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

概述

代理模式是一种结构型设计模式,它为另一个对象提供一个替身或占位符,以控制对这个对象的访问。

模式结构

代理类型

  1. 静态代理:编译时确定
  2. 动态代理:运行时生成
  3. 虚拟代理:延迟创建开销大的对象
  4. 保护代理:控制访问权限
  5. 缓存代理:缓存结果

静态代理

// 主题接口
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("photo.jpg");
// 第一次调用时才加载
image.display();

动态代理

JDK 动态代理

// 接口
public interface UserService {
    void addUser(String name);
    void deleteUser(Long id);
}

// 实现
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }
    
    @Override
    public void deleteUser(Long id) {
        System.out.println("删除用户: " + id);
    }
}

// 代理处理器
public class LogInvocationHandler implements InvocationHandler {
    private Object target;
    
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[LOG] 调用方法: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("[LOG] 方法结束: " + method.getName());
        return result;
    }
}

// 使用
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new LogInvocationHandler(target)
);

proxy.addUser("张三");

CGLIB 动态代理

// CGLIB 代理(无需接口)
public class CglibProxy implements MethodInterceptor {
    
    public Object createProxy(Class<?> targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object obj, Method method, 
                           Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB] 前置处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("[CGLIB] 后置处理");
        return result;
    }
}

// 使用
CglibProxy cglibProxy = new CglibProxy();
UserService proxy = (UserService) cglibProxy.createProxy(UserServiceImpl.class);
proxy.addUser("李四");

框架中的应用

Spring AOP

@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        // 事务管理由代理实现
    }
    
    @Cacheable("orders")
    public Order getOrder(Long id) {
        // 缓存由代理实现
    }
}

// Spring 会自动创建代理
// 1. 如果有接口,使用 JDK 动态代理
// 2. 如果没有接口使用 CGLIB 代理

JPA 延迟加载

@Entity
public class Department {
    @OneToMany(fetch = FetchType.LAZY)
    private List<Employee> employees; // 代理对象
}

// employees 在访问时才真正加载
Department dept = departmentRepository.findById(1L);
// 此时 employees 是代理对象
for (Employee e : dept.getEmployees()) {
    // 第一次遍历时才真正查询数据库
    System.out.println(e.getName());
}

MyBatis Mapper

// MyBatis 使用代理实现 Mapper 接口
@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User findById(Long id);
}

// 实际使用时,MyBatis 返回的是代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// mapper 是代理对象实际执行时调用 SqlSession

实际应用:权限控制代理

public interface DocumentService {
    void readDocument(String docId);
    void writeDocument(String docId, String content);
    void deleteDocument(String docId);
}

public class SecurityProxy implements DocumentService {
    private DocumentService target;
    private User currentUser;
    
    public SecurityProxy(DocumentService target, User user) {
        this.target = target;
        this.currentUser = user;
    }
    
    @Override
    public void readDocument(String docId) {
        if (hasPermission("READ")) {
            target.readDocument(docId);
        } else {
            throw new SecurityException("无读取权限");
        }
    }
    
    @Override
    public void writeDocument(String docId, String content) {
        if (hasPermission("WRITE")) {
            target.writeDocument(docId, content);
        } else {
            throw new SecurityException("无写入权限");
        }
    }
    
    private boolean hasPermission(String permission) {
        return currentUser.getPermissions().contains(permission);
    }
}

缓存代理

public class CacheProxy implements UserService {
    private UserService target;
    private Map<Long, User> cache = new ConcurrentHashMap<>();
    
    @Override
    public User getUser(Long id) {
        User user = cache.get(id);
        if (user == null) {
            user = target.getUser(id);
            cache.put(id, user);
        }
        return user;
    }
    
    @Override
    public void updateUser(User user) {
        target.updateUser(user);
        cache.put(user.getId(), user);
    }
}

代理 vs 装饰器

代理装饰器
控制访问添加功能
目标对象通常隐藏目标对象可灵活组合
侧重访问控制侧重功能扩展

优缺点

优点缺点
职责分离增加复杂度
保护目标对象可能降低性能
扩展目标功能

总结

代理模式是 Spring AOP、Hibernate 延迟加载等框架的基础。理解 JDK 动态代理和 CGLIB 代理的区别对使用 Spring 框架很重要。