代理模式基础概念
代理模式是一种结构型设计模式,其核心思想是通过创建一个代理对象来控制对另一个真实对象的访问。代理对象在客户端和真实对象之间起到中介作用,允许在不改变真实对象的前提下,对其进行增强或控制。
代理模式的核心组件
- 主题接口 (Subject) - 定义真实对象和代理对象的共同接口,客户端通过该接口访问真实对象
- 真实主题 (RealSubject) - 实现主题接口,是实际要被代理的对象
- 代理 (Proxy) - 实现主题接口,持有真实主题的引用,在调用真实主题方法前后可以添加额外逻辑
静态代理实现
静态代理是指在编译时就已经确定代理类和被代理类的关系,代理类需要手动编写。
// 主题接口
interface Image {
void display();
void resize();
}
// 真实主题
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
@Override
public void resize() {
System.out.println("Resizing " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
// 代理类
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);
}
// 可以在调用真实对象方法前后添加额外逻辑
System.out.println("Before displaying");
realImage.display();
System.out.println("After displaying");
}
@Override
public void resize() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
System.out.println("Before resizing");
realImage.resize();
System.out.println("After resizing");
}
}
// 客户端代码
public class StaticProxyClient {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 第一次调用display,会加载图片并显示
System.out.println("First call to display:");
image.display();
// 第二次调用display,不会重新加载图片
System.out.println("\nSecond call to display:");
image.display();
// 调用resize方法
System.out.println("\nCall to resize:");
image.resize();
}
}
动态代理实现
动态代理是指在运行时通过反射机制动态生成代理类,无需手动编写。Java 提供了java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
来实现动态代理。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 主题接口
interface UserService {
void createUser(String username);
void deleteUser(int userId);
String getUser(int userId);
}
// 真实主题
class UserServiceImpl implements UserService {
@Override
public void createUser(String username) {
System.out.println("Creating user: " + username);
}
@Override
public void deleteUser(int userId) {
System.out.println("Deleting user with ID: " + userId);
}
@Override
public String getUser(int userId) {
System.out.println("Getting user with ID: " + userId);
return "User" + userId;
}
}
// 动态代理处理器
class LoggingHandler implements InvocationHandler {
private final Object target; // 真实对象
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用前的日志记录
System.out.println("Before method: " + method.getName());
if (args != null) {
for (int i = 0; i < args.length; i++) {
System.out.println(" Arg " + (i + 1) + ": " + args[i]);
}
}
// 调用真实对象的方法
Object result = method.invoke(target, args);
// 方法调用后的日志记录
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端代码
public class DynamicProxyClient {
public static void main(String[] args) {
// 创建真实对象
UserService userService = new UserServiceImpl();
// 创建InvocationHandler
InvocationHandler handler = new LoggingHandler(userService);
// 创建动态代理对象
UserService proxyService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[]{UserService.class},
handler
);
// 调用代理对象的方法
proxyService.createUser("John Doe");
System.out.println();
String user = proxyService.getUser(123);
System.out.println("Returned user: " + user);
System.out.println();
proxyService.deleteUser(123);
}
}
CGLIB 代理实现
CGLIB 是一个强大的、高性能的代码生成库,可以在运行时扩展 Java 类与实现 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;
// 没有实现接口的目标类
class CustomerService {
public void saveCustomer(String name) {
System.out.println("Saving customer: " + name);
}
public String getCustomer(int id) {
System.out.println("Getting customer with ID: " + id);
return "Customer" + id;
}
}
// CGLIB方法拦截器
class CustomerServiceInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
// 调用父类(即目标类)的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端代码
public class CglibProxyClient {
public static void main(String[] args) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置要代理的目标类
enhancer.setSuperclass(CustomerService.class);
// 设置回调函数
enhancer.setCallback(new CustomerServiceInterceptor());
// 创建代理对象
CustomerService proxy = (CustomerService) enhancer.create();
// 调用代理对象的方法
proxy.saveCustomer("Alice");
System.out.println();
String customer = proxy.getCustomer(456);
System.out.println("Returned customer: " + customer);
}
}
代理模式的应用场景
- 远程代理 - 为一个位于不同地址空间的对象提供本地代理
- 虚拟代理 - 延迟创建开销大的对象,如图片懒加载
- 保护代理 - 控制对原始对象的访问,用于权限管理
- 缓存代理 - 缓存对资源的访问结果,提高性能
- 智能引用代理 - 在访问对象时附加额外操作,如引用计数
静态代理与动态代理的对比
特性 | 静态代理 | 动态代理 |
---|---|---|
代理类创建时间 | 编译时 | 运行时 |
代码复杂度 | 高,需手动编写每个代理类 | 低,通过反射动态生成 |
可维护性 | 低,接口修改时需同步修改代理类 | 高,自动适配接口变化 |
灵活性 | 低,只能代理特定接口或类 | 高,可以代理任意实现接口的类 |
性能 | 稍高,无需反射 | 稍低,依赖反射调用 |
适用场景 | 简单场景,代理类数量少 | 复杂场景,需要灵活的代理机制 |
代理模式的优缺点
优点:
- 降低耦合度 - 客户端与真实对象解耦
- 增强扩展性 - 可以在不修改真实对象的情况下增强功能
- 控制访问 - 可以对真实对象的访问进行控制
- 提高性能 - 通过缓存等机制提升性能
缺点:
- 增加系统复杂度 - 引入代理对象,可能使系统变得复杂
- 性能开销 - 动态代理使用反射,可能带来性能损失
- 调试困难 - 代理逻辑可能分散在多个地方,增加调试难度
使用代理模式的注意事项
- 合理选择代理类型 - 根据需求选择静态代理、动态代理或 CGLIB 代理
- 接口设计 - 设计良好的主题接口,确保代理和真实对象行为一致
- 代理链 - 可以组合多个代理形成代理链,实现更复杂的功能
- 性能考虑 - 对于性能敏感的应用,需谨慎使用动态代理
- 兼容性 - CGLIB 代理不能用于 final 类或方法,因为无法被继承
代理模式是一种非常实用的设计模式,它通过引入代理对象来控制对真实对象的访问,提供了增强功能、控制访问和提高性能的能力。在实际开发中,动态代理和 CGLIB 代理更为常用,它们提供了更灵活的代理机制。