代理模式是 Java 设计模式中的一种结构型模式,其主要思想是通过引入一个代理对象来间接访问目标对象。这种方式可以在不改动原始类代码的基础上,对目标对象的功能进行增强,例如实现权限控制、日志记录或性能监控等功能,同时还能隐藏目标对象的具体实现细节。
一、代理模式的三大核心角色
- 抽象主题(Subject):作为代理对象和目标对象共同遵循的接口,它定义了两者对外暴露的方法,使客户端可以通过统一的方式进行调用。
- 目标对象(RealSubject):实现了抽象主题接口,负责具体的业务逻辑执行,是实际的服务提供者。
- 代理对象(Proxy):同样实现抽象主题接口,并持有目标对象的引用,在调用真实方法前后可插入额外处理逻辑。
二、代理模式的主要分类与实现方式
根据代理类生成的时机和机制不同,Java 中的代理模式可分为静态代理和动态代理两种形式。其中动态代理又包括 JDK 动态代理与 CGLIB 动态代理。
1. 静态代理
静态代理在编译阶段就已经确定了代理关系,代理类由程序员手动编写并编译成字节码文件。
实现流程如下:
- 定义公共接口(抽象主题);
- 创建目标类实现该接口;
- 创建代理类也实现同一接口,并在内部维护对目标对象的引用,从而在方法调用时添加增强逻辑。
// 1. 抽象主题接口
public interface UserService {
void addUser(String username);
}
// 2. 目标对象:实现抽象主题
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户:" + username);
}
}
// 3. 代理对象:增强目标对象功能
public class UserServiceProxy implements UserService {
// 持有目标对象引用
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
// 前置增强:日志记录
System.out.println("【日志】开始添加用户,用户名:" + username);
// 调用目标对象方法
target.addUser(username);
// 后置增强:结果校验
System.out.println("【日志】用户添加完成");
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = new UserServiceProxy(target);
proxy.addUser("张三");
}
}
优点与局限性:
- 优点:结构清晰,易于理解,不会侵入原有业务逻辑;
- 缺点:每个目标类都需要对应一个代理类,当接口方法增加时,代理类需同步修改,维护成本较高;难以应对大量类的代理需求。
2. 动态代理
动态代理在程序运行期间动态生成代理类,无需预先编写代理代码,具备更高的灵活性,适用于需要为多个类统一添加横切逻辑的场景。
(1)JDK 动态代理
JDK 动态代理依赖于 Java 的反射机制,要求目标类必须实现至少一个接口,因为代理类会实现相同的接口以保证行为一致性。其核心组件包括:
InvocationHandler接口 —— 用于定义拦截逻辑;Proxy类 —— 提供静态方法生成代理实例。
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
实现步骤:
- 自定义类实现
InvocationHandler接口,并重写invoke方法,在其中编写前置/后置操作及目标方法的调用逻辑; - 使用
Proxy.newProxyInstance()方法创建代理对象。
invoke
Proxy.newProxyInstance()
// 1. 抽象主题接口(复用上述UserService)
// 2. 目标对象(复用上述UserServiceImpl)
// 3. 自定义InvocationHandler
public class MyInvocationHandler implements InvocationHandler {
// 目标对象(通用类型,适配不同接口)
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:权限校验
System.out.println("【权限校验】检查是否有权限调用" + method.getName() + "方法");
// 调用目标对象方法
Object result = method.invoke(target, args);
// 后置增强:性能监控
System.out.println("【性能监控】" + method.getName() + "方法执行耗时:10ms");
return result;
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
// 生成动态代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
new MyInvocationHandler(target) // 自定义InvocationHandler
);
proxy.addUser("李四");
}
}
特点总结:
- 基于接口编程,实现了解耦;
- 代理类在运行期自动生成,无需手动编码;
- 限制在于目标类必须实现接口,否则无法应用此方式。
(2)CGLIB 动态代理
CGLIB 是一个基于 ASM 框架的字节码生成库,能够在运行时动态生成目标类的子类来实现代理功能,因此不要求目标类实现接口。
实现步骤:
- 引入 CGLIB 依赖(常见框架如 Spring 已内置支持);
- 定义类实现
MethodInterceptor接口; - 利用
Enhancer类创建代理实例。
intercept
// 1. 目标类(无需实现接口)
public class OrderService {
public void createOrder(String orderNo) {
System.out.println("创建订单:" + orderNo);
}
}
// 2. 自定义MethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置增强:日志记录
System.out.println("【CGLIB代理】调用方法:" + method.getName());
// 调用目标对象方法
Object result = proxy.invokeSuper(obj, args);
// 后置增强:结果处理
System.out.println("【CGLIB代理】方法执行完成");
return result;
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
// 创建Enhancer对象,用于生成代理类
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(OrderService.class);
// 设置回调(增强逻辑)
enhancer.setCallback(new MyMethodInterceptor());
// 生成代理对象
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("ORDER_123456");
}
}
特性说明:
- 无需接口约束,适用范围更广;
- 通过继承方式实现代理,故目标类不能声明为 final;
- 性能优于 JDK 动态代理(尤其在早期版本),但在 JDK 8 及以后差距逐渐缩小。
三、典型应用场景
- Spring AOP:Spring 框架底层结合使用 JDK 动态代理(对接口类)与 CGLIB 动态代理(对无接口类),实现事务管理、日志记录、缓存等横切关注点的统一织入。
- 远程代理:如 Dubbo 等 RPC 框架中,客户端通过本地代理对象调用远程服务方法,屏蔽底层网络通信复杂性。
- 虚拟代理:用于延迟加载重型资源,例如 Hibernate 的懒加载机制,通过代理控制真实数据的初始化时机。
- 保护代理:在权限控制系统中,通过代理拦截非法访问请求,确保只有授权用户才能调用特定方法。
四、三种代理方式对比分析
| 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|---|
| 实现方式 | 手动编写代理类 | 反射 + 接口 | 字节码生成 + 继承 |
| 目标类要求 | 无需强制实现接口 | 必须实现接口 | 不能为 final,无需接口 |
| 灵活性 | 低(需逐一手动维护) | 高(运行期动态生成) | 高(运行期动态生成) |
| 性能表现 | 高(编译期已确定) | 中(存在反射开销) | 高(直接生成字节码) |
总结
代理模式通过引入中间层——代理对象,实现了对目标对象访问的间接控制。这种设计既保障了原始类的封装性,又提供了良好的扩展能力。静态代理适合逻辑简单、变动较少的场景;而动态代理因其高度灵活,在现代框架开发中占据重要地位,尤其是 Spring AOP 等基础设施中的广泛应用。实际项目中,应结合目标类是否实现接口、性能要求以及可维护性等因素,合理选择合适的代理技术方案。


雷达卡


京公网安备 11010802022788号







