文章目录
目录
前言
代理模式是设计模式中结构型模式的一种,它的核心思想是:通过一个 “代理对象” 来间接访问 “目标对象”,并在访问前后添加额外操作,从而实现对目标对象的控制或功能增强。今天我们从代理模式开始了解,基于代理模式去深入了解静态代理模式和动态代理模式。
一、为什么需要代理模式?
想象一个场景:你想租房,但不想直接和房东打交道(比如需要筛选房源、签订合同、处理售后等),这时可以找中介 —— 中介就是 “代理”,它帮你对接房东(目标对象),同时还能提供额外服务(如房源筛选、合同审核)。
在程序中,代理模式的核心价值在于:
- 控制访问:比如限制某些用户访问敏感方法;
- 功能增强:在不修改目标对象代码的前提下,添加日志、事务、缓存等通用逻辑(符合 “开闭原则”);
- 隔离复杂性:比如远程代理(RPC)中,代理对象封装网络通信细节,让调用者像调用本地方法一样使用远程服务。
二、代理模式的核心角色
代理模式包含 3 个核心角色,缺一不可:
角色 | 定义 | 举例 |
---|---|---|
抽象主题(Subject) | 定义目标对象和代理对象的共同接口,确保代理对象可以替代目标对象。 | UserService 接口 |
真实主题(Real Subject) | 被代理的对象,包含核心业务逻辑。 | UserServiceImpl (实现UserService ) |
代理主题(Proxy) | 实现抽象主题接口,内部持有真实主题的引用,在调用真实主题方法前后添加增强逻辑。 | UserServiceProxy (实现UserService ) |
三、静态代理:手动编写代理类
静态代理是最简单的代理实现方式:代理类在编译期手动编写完成,与目标类实现相同的接口。
案例:给用户服务添加日志记录
假设我们有一个 “用户服务”,需要在调用其方法时自动记录日志(调用时间、方法名等),但不想修改原有服务的代码。
1. 定义抽象主题(接口)
// 抽象主题:定义用户服务的接口
public interface UserService {
void addUser(String username); // 添加用户
void deleteUser(String username); // 删除用户
}
2. 实现真实主题(目标类)
// 真实主题:包含核心业务逻辑
public 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. 实现代理主题(代理类)
import java.util.Date;
// 代理主题:增强目标类的方法
public class UserServiceProxy implements UserService {
// 持有真实主题的引用(通过构造器传入)
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
// 增强逻辑:方法调用前记录日志
System.out.println("===== 日志开始 =====");
System.out.println("时间:" + new Date());
System.out.println("调用方法:addUser,参数:" + username);
// 调用真实主题的核心业务
target.addUser(username);
// 增强逻辑:方法调用后记录日志
System.out.println("方法执行完成");
System.out.println("===== 日志结束 =====");
}
@Override
public void deleteUser(String username) {
// 同样添加增强逻辑
System.out.println("===== 日志开始 =====");
System.out.println("时间:" + new Date());
System.out.println("调用方法:deleteUser,参数:" + username);
target.deleteUser(username);
System.out.println("方法执行完成");
System.out.println("===== 日志结束 =====");
}
}
4. 使用代理对象
public class Main {
public static void main(String[] args) {
// 1. 创建真实主题对象(目标对象)
UserService userService = new UserServiceImpl();
// 2. 创建代理对象(传入目标对象)
UserService proxy = new UserServiceProxy(userService);
// 3. 调用代理对象的方法(实际会触发增强逻辑 + 核心业务)
proxy.addUser("张三");
System.out.println("-----");
proxy.deleteUser("李四");
}
}
运行结果
===== 日志开始 =====
时间:Wed Sep 10 16:30:00 CST 2025
调用方法:addUser,参数:张三
【核心业务】添加用户:张三
方法执行完成
===== 日志结束 =====
-----
===== 日志开始 =====
时间:Wed Sep 10 16:30:00 CST 2025
调用方法:deleteUser,参数:李四
【核心业务】删除用户:李四
方法执行完成
===== 日志结束 =====
静态代理的优缺点
- 优点:简单直观,逻辑清晰,无需依赖框架;
- 缺点:
- 代码冗余:每一个目标类都需要对应一个代理类(如果有 100 个目标类,就需要 100 个代理类);
- 维护成本高:当接口新增 / 修改方法时,所有代理类都需要同步修改(违反 “开闭原则”)。
四、动态代理:运行时自动生成代理类
为了解决静态代理的缺点,动态代理应运而生:代理类在程序运行时动态生成(无需手动编写),同一个代理逻辑可以复用在多个目标类上。
Java 中常见的动态代理实现有两种:
1. JDK 动态代理(基于接口)
JDK 动态代理是 Java 原生支持的代理方式,核心依赖java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
,要求目标类必须实现接口。
核心原理:
- 运行时,
Proxy
类根据目标类的接口动态生成代理类(类名通常为$ProxyN
); - 所有对代理对象的方法调用,都会被转发到
InvocationHandler
的invoke()
方法; - 在
invoke()
方法中统一编写增强逻辑(如日志),再通过反射调用目标方法。
代码示例(复用上面的UserService
):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
// 1. 定义增强逻辑处理器(实现InvocationHandler)
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("===== 日志开始 =====");
System.out.println("时间:" + new Date());
System.out.println("调用方法:" + method.getName());
System.out.println("参数:" + (args != null ? args[0] : "无"));
// 调用目标对象的方法(核心业务)
Object result = method.invoke(target, args);
// 增强逻辑:后置处理
System.out.println("方法执行完成");
System.out.println("===== 日志结束 =====");
return result;
}
}
// 2. 使用动态代理
public class DynamicProxyDemo {
public static void main(String[] args) {
// 目标对象
UserService target = new UserServiceImpl();
// 生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
new LogInvocationHandler(target) // 增强处理器
);
// 调用代理方法
proxy.addUser("王五");
System.out.println("-----");
proxy.deleteUser("赵六");
}
}
运行结果与静态代理一致,但无需手动编写UserServiceProxy
类—— 代理类在运行时自动生成。
2. CGLIB 动态代理(基于继承)
CGLIB(Code Generation Library)是第三方库,不要求目标类实现接口,而是通过 “继承目标类生成子类” 作为代理类(子类重写父类方法,添加增强逻辑)。
核心原理:
- 基于 ASM 字节码技术,在运行时生成目标类的子类(代理类);
- 代理类重写目标类的非 final 方法,在方法中添加增强逻辑;
- 由于是继承,目标类不能被
final
修饰(否则无法继承),目标方法也不能是final
(否则无法重写)。
代码示例(目标类无需实现接口):
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Date;
// 1. 目标类(无需实现接口)
class OrderService {
public void createOrder(String orderNo) {
System.out.println("【核心业务】创建订单:" + orderNo);
}
}
// 2. 定义增强逻辑(实现MethodInterceptor)
class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 增强逻辑:前置处理
System.out.println("===== 日志开始 =====");
System.out.println("时间:" + new Date());
System.out.println("调用方法:" + method.getName());
System.out.println("参数:" + (args != null ? args[0] : "无"));
// 调用目标类的方法(通过方法代理)
Object result = methodProxy.invokeSuper(proxy, args);
// 增强逻辑:后置处理
System.out.println("方法执行完成");
System.out.println("===== 日志结束 =====");
return result;
}
}
// 3. 使用CGLIB生成代理
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class); // 设置父类(目标类)
enhancer.setCallback(new LogMethodInterceptor()); // 设置增强逻辑
// 生成代理对象(目标类的子类)
OrderService proxy = (OrderService) enhancer.create();
// 调用代理方法
proxy.createOrder("ORD_001");
}
}
运行结果:
===== 日志开始 =====
时间:Wed Sep 10 17:00:00 CST 2025
调用方法:createOrder
参数:ORD_001
【核心业务】创建订单:ORD_001
方法执行完成
===== 日志结束 =====
五、静态代理 vs 动态代理
维度 | 静态代理 | 动态代理(JDK/CGLIB) |
---|---|---|
代理类生成时机 | 编译期(手动编写) | 运行时(自动生成) |
代码冗余度 | 高(每个目标类对应一个代理类) | 低(一个增强逻辑可复用) |
灵活性 | 低(接口变更需同步修改代理类) | 高(接口变更无需修改代理逻辑) |
依赖 | 无 | JDK 动态代理依赖 JDK 反射;CGLIB 依赖第三方库 |
适用场景 | 简单场景(如固定少数类的增强) | 复杂场景(如 AOP、RPC 框架) |
六、代理模式的典型应用
- AOP(面向切面编程):如 Spring AOP,通过动态代理在方法前后插入日志、事务、权限等切面逻辑;
- RPC 框架:如 Dubbo,代理对象封装网络通信细节(序列化、网络调用、反序列化),让调用者透明使用远程服务;
- 延迟加载:如 Hibernate 的懒加载,通过代理对象延迟加载关联数据(用到时才查询数据库);
- 权限控制:代理对象先校验用户权限,再决定是否调用目标方法。
总结
总的来说,代理模式通过 “代理对象” 实现对 “目标对象” 的间接访问,核心价值是控制访问 + 功能增强。
- 静态代理简单但冗余,适合简单场景;
- 动态代理(JDK/CGLIB)灵活且复用性高,是框架级开发的首选(如 Spring、Dubbo)。
所以对于我们来说,理解代理模式是掌握 AOP、RPC 等核心技术的基础。好了,今天依旧是深蹲不写BUG,我们一起加油努力!!!