Java 动态代理是一种强大的机制,允许在运行时创建代理类和对象,而无需显式编写代理类的源代码。它广泛应用于 AOP(面向切面编程)、RPC 框架、事务管理等场景。
一、核心概念
- 代理模式:通过代理对象控制对目标对象的访问,在不修改目标对象的前提下增强其功能。
- 静态代理:代理类在编译期就已确定,需为每个目标类手动编写代理类。
- 动态代理:代理类在运行时动态生成,无需手动编写,支持灵活扩展。
静态代理案例:
// UserService.java
public interface UserService {
void createUser(String username, String password);
void deleteUser(int userId);
String getUser(int userId);
}
// UserServiceImpl.java
public class UserServiceImpl implements UserService {
@Override
public void createUser(String username, String password) {
System.out.println("创建用户: " + username);
// 实际业务逻辑
}
}
// UserServiceProxy.java
public class UserServiceProxy implements UserService {
private final UserService target; // 目标对象
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void createUser(String username, String password) {
beforeMethod("createUser");
try {
// 开启事务
beginTransaction();
// 调用目标方法
target.createUser(username, password);
// 提交事务
commitTransaction();
} catch (Exception e) {
// 回滚事务
rollbackTransaction();
throw e;
}
afterMethod("createUser");
}
// 增强方法:日志记录
private void beforeMethod(String methodName) {
System.out.println("[日志] 开始调用方法: " + methodName);
}
private void afterMethod(String methodName) {
System.out.println("[日志] 方法调用结束: " + methodName);
}
// 增强方法:事务管理
private void beginTransaction() {
System.out.println("[事务] 开启事务");
}
private void commitTransaction() {
System.out.println("[事务] 提交事务");
}
private void rollbackTransaction() {
System.out.println("[事务] 回滚事务");
}
}
// 使用代理类 Main.java
public class Main {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = new UserServiceProxy(target);
// 调用代理方法
proxy.createUser("test", "123456");
System.out.println();
}
}
二、Java 动态代理的两种实现
Java 提供了两种动态代理机制:
- JDK 动态代理:基于接口的代理,要求目标对象实现至少一个接口。
- CGLIB 动态代理:基于继承的代理,通过生成子类覆盖方法实现,不要求接口。
三、JDK 动态代理详解
1. 核心 API
java.lang.reflect.Proxy
:生成代理对象的核心类。java.lang.reflect.InvocationHandler
:处理代理方法调用的接口。
2. 实现步骤
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 定义接口
interface UserService {
String getName(int id);
void updateName(int id, String name);
}
// 2. 实现接口
class UserServiceImpl implements UserService {
@Override
public String getName(int id) {
System.out.println("获取用户姓名,ID: " + id);
return "张三";
}
@Override
public void updateName(int id, String name) {
System.out.println("更新用户姓名,ID: " + id + ",新姓名: " + name);
}
}
// 3. 实现InvocationHandler
class LoggingHandler implements InvocationHandler {
private final Object target; // 目标对象
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用前增强
System.out.println("开始调用方法: " + method.getName());
if (args != null) {
System.out.println("参数: " + java.util.Arrays.toString(args));
}
// 调用目标方法
Object result = method.invoke(target, args);
// 方法调用后增强
System.out.println("方法调用结束,返回值: " + result);
return result;
}
}
// 4. 创建代理对象并使用
public class JdkProxyExample {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建InvocationHandler
InvocationHandler handler = new LoggingHandler(target);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[]{UserService.class},
handler
);
// 调用代理方法
String name = proxy.getName(1);
proxy.updateName(1, "李四");
}
}
3. 关键方法解析
-
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:loader
:定义代理类的类加载器。interfaces
:代理类要实现的接口列表。h
:调用处理器,处理代理方法的调用。
-
InvocationHandler.invoke(Object proxy, Method method, Object[] args)
:proxy
:代理对象本身。method
:被调用的方法。args
:方法参数。
四、CGLIB 动态代理详解
1. 核心依赖
CGLIB 是第三方库,需添加依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2. 实现步骤
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 1. 定义目标类(无需实现接口)
class UserService {
public String getName(int id) {
System.out.println("获取用户姓名,ID: " + id);
return "张三";
}
public void updateName(int id, String name) {
System.out.println("更新用户姓名,ID: " + id + ",新姓名: " + name);
}
}
// 2. 实现MethodInterceptor
class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 方法调用前增强
System.out.println("开始调用方法: " + method.getName());
if (args != null) {
System.out.println("参数: " + java.util.Arrays.toString(args));
}
// 调用目标方法(两种方式)
// 方式一:通过代理对象调用父类方法(推荐)
Object result = proxy.invokeSuper(obj, args);
// 方式二:通过反射调用目标对象方法(可能导致无限递归)
// Object result = method.invoke(obj, args);
// 方法调用后增强
System.out.println("方法调用结束,返回值: " + result);
return result;
}
}
// 3. 创建代理对象并使用
public class CglibProxyExample {
public static void main(String[] args) {
// 创建Enhancer
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 设置回调
enhancer.setCallback(new LoggingInterceptor());
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 调用代理方法
String name = proxy.getName(1);
proxy.updateName(1, "李四");
}
}
3. 关键类解析
Enhancer
:CGLIB 的核心类,用于生成代理对象。MethodInterceptor
:方法拦截器,类似于 JDK 的 InvocationHandler。MethodProxy
:方法代理对象,用于调用父类方法。
五、JDK 代理 vs CGLIB 代理
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理机制 | 基于接口 | 基于继承(生成子类) |
目标对象要求 | 必须实现接口 | 无需实现接口,可以是普通类 |
性能 | 调用开销较大(反射) | 调用开销较小(直接调用) |
生成类大小 | 较小 | 较大(生成子类) |
方法限制 | 只能代理接口方法 | 不能代理 final、static 方法 |
六、注意事项
- 性能考虑:反射调用开销较大,频繁调用时需注意性能。
- final 方法:CGLIB 无法代理 final 方法,因为无法被子类覆盖。
- 类加载问题:动态生成的代理类可能导致类加载器泄漏。
- 调试困难:代理逻辑增加了代码复杂度,调试时需注意调用栈。