Java 代理模式详解
代理模式是 结构型设计模式 的一种,核心思想是:通过一个 "代理对象" 间接访问目标对象,在不修改目标对象代码的前提下,对目标对象的行为进行增强或控制(如日志记录、权限校验、事务管理等)。
核心角色
代理模式包含 3 个核心角色,缺一不可:
-
抽象主题(Subject):定义目标对象和代理对象的共同接口(或抽象类),是代理模式的 "约定",确保代理对象可替代目标对象。
-
目标主题(Target):被代理的真实对象,负责执行核心业务逻辑。
-
代理主题(Proxy):持有目标对象的引用,实现抽象主题接口,在调用目标对象方法前后,添加增强逻辑(如日志、权限校验)。
代理模式的 3 种实现方式
Java 中代理模式主要分为 静态代理 和 动态代理(JDK 动态代理、CGLIB 动态代理),下面逐一详解。
一、静态代理(Static Proxy)
静态代理是 编译期就生成代理类(手动编写代理类代码),代理类和目标类的关系在编译时确定。
实现步骤
-
定义抽象主题接口(Subject);
-
实现目标类(Target),重写抽象接口方法;
-
实现代理类(Proxy),持有目标对象引用,重写抽象接口方法(调用目标方法 + 增强逻辑)。
代码示例:日志增强
1. 抽象主题接口(Subject)
// 抽象主题:定义核心业务方法
public interface UserService {
void addUser(String username); // 新增用户(核心业务)
}
2. 目标类(Target)
// 目标对象:实现核心业务逻辑
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
// 核心业务:新增用户
System.out.println("核心逻辑:新增用户 -> " + username);
}
}
3. 代理类(Proxy)
// 代理对象:持有目标对象引用,添加增强逻辑(日志记录)
public class UserServiceProxy implements UserService {
// 持有目标对象的引用
private final UserService target;
// 构造方法注入目标对象
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
// 增强逻辑:方法执行前(日志记录)
System.out.println("日志:开始执行 addUser 方法,参数:" + username);
// 调用目标对象的核心方法
target.addUser(username);
// 增强逻辑:方法执行后(日志记录)
System.out.println("日志:addUser 方法执行完毕\n");
}
}
4. 测试类
public class ProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserServiceImpl();
// 2. 创建代理对象(注入目标对象)
UserService proxy = new UserServiceProxy(target);
// 3. 通过代理对象调用方法(间接访问目标对象)
proxy.addUser("张三");
proxy.addUser("李四");
}
}
输出结果
日志:开始执行 addUser 方法,参数:张三
核心逻辑:新增用户 -> 张三
日志:addUser 方法执行完毕
日志:开始执行 addUser 方法,参数:李四
核心逻辑:新增用户 -> 李四
日志:addUser 方法执行完毕
静态代理的优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,无额外依赖,易理解 | 代理类与目标类强耦合:目标类新增方法时,代理类需同步修改 |
| 增强逻辑清晰,无运行时开销 | 代码冗余:每个目标类都需对应一个代理类,类数量爆炸 |
| 不修改目标对象代码,符合开闭原则 | 灵活性差:无法动态切换增强逻辑 |
二、动态代理(Dynamic Proxy)
动态代理是 运行时动态生成代理类(无需手动编写代理类),代理类和目标类的关系在运行时确定,灵活性更高。
Java 中动态代理主要有两种实现:
-
JDK 动态代理:JDK 自带(java.lang.reflect 包),基于接口实现;
-
CGLIB 动态代理:第三方库(需导入依赖),基于继承目标类实现。
1. JDK 动态代理(重点)
核心原理
-
依赖
java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口; -
代理类必须实现目标类的 所有接口(JDK 动态代理本质是 "接口代理");
-
运行时通过
Proxy.newProxyInstance()动态生成代理对象; -
增强逻辑统一写在
InvocationHandler的invoke()方法中。
实现步骤
-
定义抽象主题接口(同静态代理);
-
实现目标类(同静态代理);
-
实现
InvocationHandler接口,编写增强逻辑; -
通过
Proxy.newProxyInstance()生成代理对象并调用方法。
代码示例:日志 + 权限增强
1. 抽象主题接口 + 目标类(复用静态代理的 UserService 和 UserServiceImpl)
2. 实现 InvocationHandler(增强逻辑处理器)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 增强逻辑处理器:实现 InvocationHandler,统一处理所有代理方法
public class MyInvocationHandler implements InvocationHandler {
// 持有目标对象的引用
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* 所有代理方法的调用都会转发到 invoke() 方法
* @param proxy 代理对象(一般不用)
* @param method 目标方法(被调用的方法)
* @param args 目标方法的参数
* @return 目标方法的返回值
* @throws Throwable 目标方法可能抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强逻辑 1:方法执行前(权限校验)
System.out.println("权限校验:验证用户是否有权限执行 " + method.getName() + " 方法");
// 增强逻辑 2:方法执行前(日志记录)
System.out.println("日志:开始执行 " + method.getName() + " 方法,参数:" + (args != null ? args[0] : "无"));
// 调用目标对象的核心方法(反射调用)
Object result = method.invoke(target, args);
// 增强逻辑 3:方法执行后(日志记录)
System.out.println("日志:" + method.getName() + " 方法执行完毕\n");
return result; // 返回目标方法的执行结果
}
}
3. 测试类(生成代理对象并调用)
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserServiceImpl();
// 2. 创建增强逻辑处理器(注入目标对象)
InvocationHandler handler = new MyInvocationHandler(target);
// 3. 动态生成代理对象
// 参数 1:目标对象的类加载器
// 参数 2:目标对象实现的所有接口(JDK 代理基于接口)
// 参数 3:增强逻辑处理器
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
// 4. 通过代理对象调用方法
proxy.addUser("王五");
proxy.addUser("赵六");
}
}
输出结果
权限校验:验证用户是否有权限执行 addUser 方法
日志:开始执行 addUser 方法,参数:王五
核心逻辑:新增用户 -> 王五
日志:addUser 方法执行完毕
权限校验:验证用户是否有权限执行 addUser 方法
日志:开始执行 addUser 方法,参数:赵六
核心逻辑:新增用户 -> 赵六
日志:addUser 方法执行完毕
JDK 动态代理的优缺点
| 优点 | 缺点 |
|---|---|
| 无需手动编写代理类,灵活高效 | 只能代理 实现了接口 的目标类(无法代理无接口的类) |
| 增强逻辑统一管理,无代码冗余 | 基于反射,有轻微运行时开销(可忽略) |
| JDK 自带,无需额外依赖 | 无法直接代理目标类的静态方法、final 方法 |
2. CGLIB 动态代理
核心原理
-
CGLIB(Code Generation Library)是一个字节码生成库,通过 继承目标类 生成代理类(子类代理);
-
无需目标类实现接口,可代理任意非 final 类(final 类无法被继承);
-
依赖
net.sf.cglib.proxy.Enhancer类和net.sf.cglib.proxy.MethodInterceptor接口; -
增强逻辑写在
MethodInterceptor的intercept()方法中。
依赖导入(Maven)
CGLIB 需手动导入依赖(Spring 框架已内置 CGLIB,若使用 Spring 可省略):
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 推荐使用最新稳定版 -->
</dependency>
实现步骤
-
定义目标类(无需实现接口);
-
实现
MethodInterceptor接口,编写增强逻辑; -
通过
Enhancer类生成代理对象并调用方法。
代码示例:日志增强(无接口目标类)
1. 目标类(无接口)
// 目标对象:无接口,非 final 类(CGLIB 基于继承)
public class OrderService {
// 核心业务方法(非 final,否则无法被代理增强)
public void createOrder(String orderNo) {
System.out.println("核心逻辑:创建订单 -> " + orderNo);
}
}
2. 实现 MethodInterceptor(增强逻辑处理器)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 增强逻辑处理器:实现 MethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor {
/**
* 所有代理方法的调用都会转发到 intercept() 方法
* @param obj 代理对象(子类实例)
* @param method 目标方法
* @param args 目标方法参数
* @param proxy 方法代理对象(用于调用目标方法,比反射更高效)
* @return 目标方法返回值
* @throws Throwable 目标方法可能抛出的异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 增强逻辑:方法执行前
System.out.println("日志:开始执行 " + method.getName() + " 方法,参数:" + args[0]);
// 调用目标对象的核心方法(两种方式)
// 方式 1:通过 MethodProxy 调用(推荐,比反射高效)
Object result = proxy.invokeSuper(obj, args);
// 方式 2:通过反射调用(同 JDK 代理)
// Object result = method.invoke(target, args);
// 增强逻辑:方法执行后
System.out.println("日志:" + method.getName() + " 方法执行完毕\n");
return result;
}
}
3. 测试类(生成代理对象并调用)
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建增强逻辑处理器
MethodInterceptor interceptor = new MyMethodInterceptor();
// 2. 通过 Enhancer 生成代理对象(子类代理)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class); // 设置父类(目标类)
enhancer.setCallback(interceptor); // 设置增强逻辑处理器
// 3. 生成代理对象(强制转换为目标类类型)
OrderService proxy = (OrderService) enhancer.create();
// 4. 通过代理对象调用方法
proxy.createOrder("ORDER_20251123");
proxy.createOrder("ORDER_20251124");
}
}
输出结果
日志:开始执行 createOrder 方法,参数:ORDER_20251123
核心逻辑:创建订单 -> ORDER_20251123
日志:createOrder 方法执行完毕
日志:开始执行 createOrder 方法,参数:ORDER_20251124
核心逻辑:创建订单 -> ORDER_20251124
日志:createOrder 方法执行完毕
CGLIB 动态代理的优缺点
| 优点 | 缺点 |
|---|---|
| 无需目标类实现接口,适用范围更广 | 依赖第三方库,需额外导入 |
| 基于字节码生成,比 JDK 代理(反射)效率更高 | 无法代理 final 类 和 final 方法(子类无法重写) |
| 可代理静态方法(需特殊配置) | 生成代理类时字节码操作复杂,初始化开销略大 |
三、三种代理模式对比
| 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|---|
| 代理方式 | 手动编写代理类 | 基于接口(反射) | 基于继承(字节码生成) |
| 目标类要求 | 需实现接口 | 需实现接口 | 无接口要求(非 final 类) |
| 依赖 | 无 | JDK 自带(java.lang.reflect) | 第三方库(cglib) |
| 灵活性 | 低(编译期确定) | 中(运行时生成) | 高(运行时生成,无接口限制) |
| 性能 | 高(无反射开销) | 中(反射调用) | 高(字节码直接调用,比 JDK 快) |
| 适用场景 | 简单场景、类数量少 | 目标类有接口(如 Spring AOP 默认) | 目标类无接口(如 MyBatis mapper 代理) |
四、代理模式的典型应用场景
-
日志记录:方法调用前后记录日志(如接口请求日志);
-
权限校验:方法执行前校验用户权限(如 Admin 接口拦截);
-
事务管理:方法执行前后开启 / 提交事务(如 Spring 声明式事务);
-
缓存控制:方法执行前查询缓存,执行后更新缓存(如 Redis 缓存);
-
远程调用:代理对象封装远程调用细节(如 RPC 框架中的服务代理);
-
延迟加载:代理对象延迟初始化目标对象(如 Hibernate 懒加载)。
经典框架应用
-
Spring AOP:默认优先使用 JDK 动态代理,目标类无接口时使用 CGLIB;
-
MyBatis:Mapper 接口代理(JDK 动态代理),无需实现类即可执行 SQL;
-
Dubbo:服务代理(支持 JDK/CGLIB),封装远程调用细节。
五、总结
代理模式的核心价值是 "解耦" :将核心业务逻辑(目标对象)与增强逻辑(日志、权限等)分离,符合 开闭原则(对扩展开放,对修改关闭)。
-
简单场景用 静态代理(易理解、无开销);
-
目标类有接口用 JDK 动态代理(无依赖、灵活);
-
目标类无接口用 CGLIB 动态代理(适用范围广、效率高)。
实际开发中,很少手动编写代理类,更多是使用框架(如 Spring AOP)封装的动态代理能力,专注于业务逻辑和增强逻辑的实现即可。