Java中的静态代理与动态代理详解
字数 1677 2025-11-11 06:58:35
Java中的静态代理与动态代理详解
1. 代理模式的基本概念
代理模式是一种设计模式,通过一个代理对象控制对原始对象的访问。代理可以在不修改原始对象的基础上,增强其功能(如添加日志、权限校验等)。代理分为两类:
- 静态代理:在编译期确定代理关系,代理类需手动编码。
- 动态代理:在运行期动态生成代理类,无需预先编写代理类代码。
2. 静态代理的实现与局限性
2.1 静态代理示例
假设有一个接口 UserService 和其实现类:
public interface UserService {
void saveUser(String user);
}
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}
静态代理类需要实现相同接口,并在内部调用原始对象的方法:
public class UserServiceProxy implements UserService {
private UserService target; // 目标对象
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void saveUser(String user) {
System.out.println("前置增强:权限校验");
target.saveUser(user); // 调用目标方法
System.out.println("后置增强:记录日志");
}
}
2.2 静态代理的缺点
- 代码冗余:每个目标类都需要对应一个代理类,难以维护。
- 灵活性差:接口变更时,代理类需同步修改。
3. 动态代理的核心机制
动态代理通过反射机制在运行时动态生成代理类。Java提供了两种实现方式:
- JDK 动态代理:基于接口生成代理类。
- CGLIB 动态代理:通过继承目标类生成子类代理(无需接口)。
4. JDK 动态代理详解
4.1 核心类与接口
java.lang.reflect.Proxy:提供创建代理对象的静态方法(如newProxyInstance)。java.lang.reflect.InvocationHandler:代理类的调用处理器,需实现invoke方法。
4.2 实现步骤
步骤1:定义调用处理器(实现 InvocationHandler):
public 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("方法调用前:" + method.getName());
Object result = method.invoke(target, args); // 反射调用目标方法
System.out.println("方法调用后:" + method.getName());
return result;
}
}
步骤2:通过 Proxy 创建代理对象:
public class Main {
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.saveUser("Alice"); // 调用代理方法
}
}
4.3 JDK 动态代理的限制
- 目标类必须实现接口,否则无法生成代理。
5. CGLIB 动态代理详解
5.1 基本原理
CGLIB(Code Generation Library)通过字节码技术生成目标类的子类,重写父类方法实现代理。
5.2 实现步骤
步骤1:添加 CGLIB 依赖(Maven):
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
步骤2:定义方法拦截器(实现 MethodInterceptor):
public class CglibProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB 前置增强");
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("CGLIB 后置增强");
return result;
}
}
步骤3:通过 Enhancer 创建代理对象:
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class); // 设置目标类为父类
enhancer.setCallback(new CglibProxyInterceptor()); // 设置拦截器
UserServiceImpl proxy = (UserServiceImpl) enhancer.create(); // 创建代理对象
proxy.saveUser("Bob");
}
}
5.3 CGLIB 的特点与限制
- 优点:无需接口,可直接代理普通类。
- 缺点:
- 无法代理
final类或final方法(无法被重写)。 - 生成代理类的过程比 JDK 动态代理稍慢。
- 无法代理
6. 静态代理与动态代理的对比
| 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|---|
| 实现方式 | 手动编写代理类 | 基于接口反射 | 基于子类继承 |
| 编译期依赖 | 需要 | 不需要 | 不需要 |
| 目标类要求 | 需实现接口 | 需实现接口 | 不能是 final 类或方法 |
| 性能 | 高(直接调用) | 中等(反射调用) | 较高(方法索引优化) |
7. 动态代理的实际应用场景
- Spring AOP:基于 JDK 动态代理(接口)和 CGLIB(无接口)实现切面编程。
- RPC 框架:代理远程方法调用,封装网络通信细节。
- 日志记录、事务管理:无侵入式增强业务方法。
通过以上步骤,你可以深入理解 Java 中代理模式的实现原理及适用场景,为后续学习 Spring 等框架打下基础。