代理模式概述
在软件开发中,代理模式是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不修改目标对象代码的前提下,增加额外的功能或控制访问。
静态代理:编译时确定的代理
什么是静态代理?
静态代理是指在编译时期就已经确定代理类与被代理类的关系。代理类和目标类实现相同的接口,代理类内部持有目标对象的引用,并在调用目标方法前后添加额外的逻辑。
静态代理的实现
让我们通过一个简单的例子来理解静态代理:
java
// 1. 定义接口
interface UserService {
void addUser(String username);
void deleteUser(String username);
}
// 2. 实现目标类
class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
// 3. 创建代理类
class UserServiceProxy implements UserService {
private UserService target; // 目标对象
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
System.out.println("【代理】开始添加用户...");
long start = System.currentTimeMillis();
// 调用目标方法
target.addUser(username);
long end = System.currentTimeMillis();
System.out.println("【代理】添加用户完成,耗时: " + (end - start) + "ms");
}
@Override
public void deleteUser(String username) {
System.out.println("【代理】开始删除用户...");
long start = System.currentTimeMillis();
// 调用目标方法
target.deleteUser(username);
long end = System.currentTimeMillis();
System.out.println("【代理】删除用户完成,耗时: " + (end - start) + "ms");
}
}
// 4. 客户端使用
public class StaticProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象,并传入目标对象
UserService proxy = new UserServiceProxy(target);
// 通过代理对象调用方法
proxy.addUser("张三");
proxy.deleteUser("李四");
}
}
静态代理的优缺点
优点:
- 简单直观,易于理解和实现
- 在不修改目标对象的前提下扩展功能
- 符合开闭原则
缺点:
- 代理类和目标类必须实现相同的接口,如果接口有多个方法,代理类需要重写所有方法
- 每个代理类只能代理一个接口,如果有多个接口需要代理,需要创建多个代理类
- 代理类在编译时就已经确定,不够灵活
动态代理:运行时生成的代理
什么是动态代理?
动态代理是指在程序运行时动态生成代理类。Java提供了两种主要的动态代理机制:
- JDK动态代理:基于接口的代理
- CGLIB动态代理:基于类继承的代理
JDK动态代理
JDK动态代理利用Java的反射机制,在运行时创建代理类。它要求目标类必须实现接口。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 定义接口和目标类(与静态代理相同)
interface OrderService {
void createOrder(String orderId);
void cancelOrder(String orderId);
}
class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String orderId) {
System.out.println("创建订单: " + orderId);
}
@Override
public void cancelOrder(String orderId) {
System.out.println("取消订单: " + orderId);
}
}
// 2. 创建InvocationHandler实现类
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 {
String methodName = method.getName();
System.out.println("【动态代理】开始执行方法: " + methodName);
long start = System.currentTimeMillis();
// 调用目标方法
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("【动态代理】方法执行完成,耗时: " + (end - start) + "ms");
return result;
}
}
// 3. 动态代理工厂类
class ProxyFactory {
public static Object createProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口
new LoggingHandler(target) // InvocationHandler
);
}
}
// 4. 客户端使用
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建目标对象
OrderService target = new OrderServiceImpl();
// 创建动态代理
OrderService proxy = (OrderService) ProxyFactory.createProxy(target);
// 通过代理对象调用方法
proxy.createOrder("ORD001");
proxy.cancelOrder("ORD002");
// 可以查看代理类的信息
System.out.println("代理类: " + proxy.getClass().getName());
System.out.println("代理类的父类: " + proxy.getClass().getSuperclass().getName());
System.out.println("代理类实现的接口: ");
for (Class<?> clazz : proxy.getClass().getInterfaces()) {
System.out.println(" - " + clazz.getName());
}
}
}
CGLIB动态代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现Java接口。与JDK动态代理不同,CGLIB可以代理没有实现接口的类。
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 1. 目标类(不需要实现接口)
class ProductService {
public void addProduct(String productName) {
System.out.println("添加产品: " + productName);
}
public void updateProduct(String productName) {
System.out.println("更新产品: " + productName);
}
}
// 2. 创建MethodInterceptor实现类
class CglibProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
System.out.println("【CGLIB代理】开始执行方法: " + methodName);
long start = System.currentTimeMillis();
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
long end = System.currentTimeMillis();
System.out.println("【CGLIB代理】方法执行完成,耗时: " + (end - start) + "ms");
return result;
}
}
// 3. CGLIB代理工厂
class CglibProxyFactory {
public static Object createProxy(Class<?> targetClass) {
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(targetClass);
// 设置回调
enhancer.setCallback(new CglibProxyInterceptor());
// 创建代理对象
return enhancer.create();
}
}
// 4. 客户端使用
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建CGLIB代理
ProductService proxy = (ProductService) CglibProxyFactory.createProxy(ProductService.class);
// 通过代理对象调用方法
proxy.addProduct("笔记本电脑");
proxy.updateProduct("智能手机");
// 可以查看代理类的信息
System.out.println("代理类: " + proxy.getClass().getName());
System.out.println("代理类的父类: " + proxy.getClass().getSuperclass().getName());
}
}
动态代理的优缺点
优点:
- 高度灵活,一个代理类可以代理多个接口
- 减少代码量,不需要为每个目标类创建代理类
- 可以在运行时决定代理哪个对象
缺点:
- JDK动态代理只能代理实现了接口的类
- CGLIB不能代理final类或final方法
- 动态代理的性能略低于静态代理(但影响通常很小)
静态代理 vs 动态代理:核心对比
| 特性 | 静态代理 | 动态代理 |
|---|---|---|
| 实现时机 | 编译时 | 运行时 |
| 实现方式 | 手动编写代理类 | 通过反射或字节码生成 |
| 灵活性 | 低,一个代理类只能代理一个接口 | 高,一个代理类可以代理多个接口 |
| 代码量 | 多,需要为每个目标类编写代理类 | 少,通过通用代码处理多个类 |
| 性能 | 略高(直接调用) | 略低(反射调用) |
| 适用场景 | 接口方法少,代理逻辑简单 | 接口方法多,代理逻辑复杂 |
实际应用场景
静态代理适用场景
- AOP(面向切面编程)的简单实现
- 权限控制
- 本地远程代理
动态代理适用场景
- Spring AOP:Spring框架使用JDK动态代理和CGLIB实现AOP
- MyBatis:Mapper接口的代理实现
- RPC框架:远程方法调用的客户端代理
- 事务管理:声明式事务的实现
- 日志记录:方法调用的日志记录
Spring框架中的代理示例
java
// Spring中常用的注解驱动代理
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Override
@LogExecutionTime // 自定义注解,通过AOP实现方法执行时间日志
public User findUserById(Long id) {
// 业务逻辑
return userRepository.findById(id);
}
@Override
@PreAuthorize("hasRole('ADMIN')") // Spring Security权限控制
public void deleteUser(Long id) {
// 业务逻辑
userRepository.deleteById(id);
}
}
总结
代理模式是Java中非常重要且实用的设计模式。静态代理简单直观,适合简单的代理需求;动态代理灵活强大,适合复杂的系统架构。理解两者的原理和适用场景,能够帮助我们在实际开发中做出更合适的技术选择。
无论是静态代理还是动态代理,它们都体现了面向对象设计的一个重要原则:对扩展开放,对修改关闭。通过代理模式,我们可以在不修改原有代码的基础上,为系统添加新的功能,这大大提高了代码的可维护性和可扩展性。