Java 代理模式:原理、实现与应用场景
·
代理模式是 Java 设计模式中的结构型模式之一,其核心思想是为目标对象提供一个代理对象,通过代理对象控制对目标对象的访问。代理模式可以在不修改目标对象代码的前提下,增强目标对象的功能(如权限控制、日志记录、性能监控等),同时隐藏目标对象的具体实现细节。
一、代理模式的核心角色
代理模式包含三个核心角色:
- 抽象主题(Subject):定义目标对象和代理对象的共同接口,确保代理对象与目标对象具有一致的行为,客户端可通过该接口统一访问。
- 目标对象(RealSubject):实现抽象主题接口,是代理对象所代表的真实业务逻辑执行者。
- 代理对象(Proxy):同样实现抽象主题接口,内部持有目标对象的引用,在调用目标对象方法的前后可以添加额外逻辑。
二、代理模式的分类与实现
根据代理对象的创建时机和实现方式,Java 中的代理模式主要分为静态代理和动态代理(JDK 动态代理、CGLIB 动态代理)。
1. 静态代理
静态代理是在编译期就确定代理类与目标类的关系,代理类由开发者手动编写,编译后生成.class 文件。
实现步骤:
- 定义抽象主题接口;
- 实现目标类(RealSubject);
- 编写代理类(Proxy),持有目标对象引用,并在方法中增强逻辑。
示例代码:
// 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. 动态代理
动态代理是在运行期动态生成代理类,无需手动编写代理类代码,能灵活处理多个目标类的代理需求。Java 中常见的动态代理方式有JDK 动态代理和CGLIB 动态代理。
(1)JDK 动态代理
JDK 动态代理基于 Java 反射机制实现,要求目标类必须实现接口(因为代理类会动态实现目标类的接口)。核心类为java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。
实现步骤:
- 定义 InvocationHandler 实现类,重写
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("李四");
}
}
JDK 动态代理的特点:
- 基于接口实现,代理类与目标类解耦;
- 无需手动编写代理类,运行期动态生成,灵活性高;
- 缺点:目标类必须实现接口,否则无法使用。
(2)CGLIB 动态代理
CGLIB(Code Generation Library)是基于 ASM 字节码生成框架实现的动态代理,无需目标类实现接口,通过继承目标类生成子类作为代理类。
实现步骤:
- 引入 CGLIB 依赖(Spring 等框架已内置);
- 定义 MethodInterceptor 实现类,重写
intercept方法; - 通过 Enhancer 类生成代理对象。
示例代码:
// 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");
}
}
CGLIB 动态代理的特点:
- 无需目标类实现接口,适用性更广;
- 通过继承实现,目标类不能是 final(否则无法生成子类);
- 性能略优于 JDK 动态代理(JDK 8 后差距缩小)。
三、代理模式的应用场景
- Spring AOP:Spring AOP 底层结合 JDK 动态代理(目标类实现接口)和 CGLIB 动态代理(目标类无接口),实现横切逻辑(如事务管理、日志、缓存)的统一增强。
- 远程代理:如 RPC 框架(Dubbo)中,客户端通过代理对象调用远程服务器的方法,隐藏网络通信细节。
- 虚拟代理:延迟加载重量级对象,如 Hibernate 的懒加载(Lazy Loading),通过代理对象延迟初始化真实数据。
- 保护代理:控制对目标对象的访问权限,如权限校验框架中,通过代理拦截未授权的方法调用。
四、静态代理与动态代理的对比
| 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|---|
| 实现方式 | 手动编写代理类 | 反射 + 接口 | 字节码生成 + 继承 |
| 目标类要求 | 需实现接口(非必须) | 必须实现接口 | 无需实现接口,不能是 final |
| 灵活性 | 低(需手动维护) | 高(运行期动态生成) | 高(运行期动态生成) |
| 性能 | 高(编译期确定) | 中(反射开销) | 高(字节码直接生成) |
总结
代理模式通过引入代理对象实现对目标对象的间接访问,既保护了目标对象的封装性,又能灵活扩展功能。静态代理适用于简单场景,动态代理则在框架开发中发挥核心作用(如 Spring AOP)。实际开发中,需根据目标类是否实现接口、性能要求等选择合适的代理方式。
更多推荐


所有评论(0)