Java动态代理通过JDK(需接口)和CGLIB(继承)在运行时生成代理类,实现方法拦截与增强,广泛应用于Spring AOP的日志、事务管理,但各有性能与使用限制。
1. 代理模式基础
在理解动态代理前,需先掌握 代理模式(Proxy Pattern) 的核心思想:
-
核心目标:通过一个 代理对象 控制对真实对象的访问,实现 增强功能(如日志、事务、权限校验)。
-
分类:
- 静态代理:手动编写代理类,需为每个被代理类创建对应的代理类。
- 动态代理:运行时动态生成代理类,无需手动编码。
2. 动态代理的核心原理
动态代理在 运行时 动态生成代理类,拦截对目标方法的调用,并在调用前后插入增强逻辑。其核心流程如下:
- 代理类生成:根据接口或父类生成代理类的字节码。
- 方法拦截:通过回调机制(如
InvocationHandler)处理目标方法的调用。 - 逻辑增强:在目标方法执行前后插入额外逻辑(如日志、事务)。
3. Java 动态代理的两种实现方式
(1) JDK 动态代理
-
依赖条件:目标类必须实现接口。
-
核心类:
java.lang.reflect.Proxy:生成代理类。java.lang.reflect.InvocationHandler:定义方法拦截逻辑。
-
实现步骤:
- 定义接口和实现类。
- 实现
InvocationHandler,编写增强逻辑。 - 通过
Proxy.newProxyInstance()生成代理对象。
代码示例:
// 1. 定义接口
public interface UserService {
void saveUser(String user);
}
// 2. 实现类
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}
// 3. 实现 InvocationHandler
public class LogInvocationHandler implements InvocationHandler {
private final 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;
}
}
// 4. 生成代理对象
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("张三");
}
}
输出:
方法调用前:saveUser
保存用户:张三
方法调用后:saveUser
(2) CGLIB 动态代理
-
依赖条件:通过继承目标类生成代理,无需接口。
-
核心库:
cglib(Spring 核心包已内置)。 -
核心类:
Enhancer:生成代理类。MethodInterceptor:定义方法拦截逻辑。
-
实现步骤:
- 定义目标类(无需实现接口)。
- 实现
MethodInterceptor,编写增强逻辑。 - 通过
Enhancer生成代理对象。
代码示例:
// 1. 目标类
public class UserService {
public void saveUser(String user) {
System.out.println("保存用户:" + user);
}
}
// 2. 实现 MethodInterceptor
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法调用前:" + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("方法调用后:" + method.getName());
return result;
}
}
// 3. 生成代理对象
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LogMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.saveUser("李四");
}
}
输出:
方法调用前:saveUser
保存用户:李四
方法调用后:saveUser
4. JDK 动态代理 vs CGLIB 动态代理
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 依赖条件 | 目标类必须实现接口 | 目标类无需实现接口 |
| 生成方式 | 基于接口生成代理类 | 通过继承目标类生成子类代理 |
| 性能 | 调用方法时反射性能较低 | 通过 FastClass 直接调用,性能更高 |
| 局限性 | 无法代理未实现接口的类 | 无法代理 final 类或 final 方法 |
| 应用场景 | 需要接口的轻量级代理 | 无接口或需要高性能代理 |
5. 动态代理在 Spring 中的应用
Spring AOP(面向切面编程)的核心机制基于动态代理,具体规则如下:
-
默认策略:
- 若目标类实现了接口 → 使用 JDK 动态代理。
- 若目标类未实现接口 → 使用 CGLIB 动态代理。
-
强制使用 CGLIB:通过
@EnableAspectJAutoProxy(proxyTargetClass = true)配置。
示例:Spring AOP 事务管理
@Service
public class UserService {
@Transactional
public void createUser(String user) {
// 数据库操作
}
}
// Spring 通过动态代理为 UserService 生成代理类,添加事务管理逻辑。
6. 动态代理的底层实现
(1) JDK 动态代理字节码生成
-
生成类名:
$Proxy0、$Proxy1... -
核心逻辑:
- 代理类继承
Proxy并实现目标接口。 - 每个方法调用转发到
InvocationHandler.invoke()。
- 代理类继承
反编译代理类示例:
public final class $Proxy0 extends Proxy implements UserService {
public $Proxy0(InvocationHandler h) { super(h); }
public final void saveUser(String user) {
try {
super.h.invoke(this,
m3, // Method 对象:UserService.saveUser
new Object[]{user});
} catch (Throwable e) { /*...*/ }
}
}
(2) CGLIB 动态代理字节码生成
-
生成类名:
UserService$$EnhancerByCGLIB$$12345678 -
核心逻辑:
- 代理类继承目标类,重写非 final 方法。
- 方法调用转发到
MethodInterceptor.intercept()。
7. 动态代理的应用场景
- 日志记录:记录方法调用参数、耗时。
- 事务管理:方法执行前开启事务,执行后提交或回滚。
- 权限校验:拦截方法调用,验证用户权限。
- 缓存优化:方法结果缓存,避免重复计算。
- RPC 框架:透明化远程方法调用(如 Dubbo、Feign)。
8. 动态代理的局限性
-
JDK 代理:无法代理未实现接口的类。
-
CGLIB 代理:
- 无法代理 final 类或 final 方法。
- 生成代理类较慢,首次调用性能开销较大。
-
自调用问题:代理对象内部方法相互调用时,增强逻辑可能失效。
9. 实战:手动实现简单动态代理
以下是一个简化版的动态代理实现逻辑:
public class SimpleProxy {
public static Object createProxy(Object target, Advice advice) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
advice.before();
Object result = method.invoke(target, args);
advice.after();
return result;
});
}
}
// 定义增强接口
interface Advice {
void before();
void after();
}
// 使用示例
UserService proxy = (UserService) SimpleProxy.createProxy(new UserServiceImpl(), new Advice() {
@Override
public void before() { System.out.println("前置增强"); }
@Override
public void after() { System.out.println("后置增强"); }
});
总结
动态代理通过 运行时生成代理类 实现方法拦截与功能增强,是 Java 高级编程和框架设计的核心技术之一。掌握其原理与实现方式,能够深入理解 Spring AOP、MyBatis 等框架的底层机制,并为开发高性能、可扩展的应用提供坚实基础。