副标题:JDK动态代理 vs CGLIB,谁更胜一筹?🎯
🎬 开场:AOP是什么?
面向切面编程
传统OOP(面向对象编程):
将功能封装成一个个对象
问题:
很多横切关注点(Cross-cutting Concerns)
散落在各个对象中
例如:日志、事务、权限、性能监控
┌───────────┐
│ UserService │ → 记录日志 📝
│ login() │ → 开启事务 💾
│ register()│ → 权限校验 🔐
│ delete() │ → 性能监控 ⏱️
└───────────┘
每个方法都要写这些重复代码 ❌
AOP(面向切面编程):
将横切关注点抽取出来,统一处理
┌───────────┐ ┌──────────┐
│ UserService │ ──→ │ 日志切面 │
│ login() │ ──→ │ 事务切面 │
│ register()│ ──→ │ 权限切面 │
│ delete() │ ──→ │ 监控切面 │
└───────────┘ └──────────┘
代码清爽,关注点分离 ✅
📚 AOP核心概念
术语解释
1. 切面(Aspect)
- 横切关注点的模块化
- 例如:日志切面、事务切面
2. 连接点(Join Point)
- 程序执行的某个点
- 例如:方法调用、方法执行
3. 切点(Pointcut)
- 匹配连接点的表达式
- 例如:execution(* com.example.service.*.*(..))
4. 通知(Advice)
- 在切点执行的动作
- 类型:Before、After、Around、AfterReturning、AfterThrowing
5. 目标对象(Target Object)
- 被代理的对象
6. 代理对象(Proxy Object)
- AOP创建的对象
7. 织入(Weaving)
- 将切面应用到目标对象的过程
通知类型
/**
* 五种通知类型
*/
@Aspect
@Component
public class LogAspect {
/**
* 前置通知(Before)
* 在目标方法执行之前执行
*/
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("【Before】方法执行前:" + methodName);
}
/**
* 后置通知(After)
* 在目标方法执行之后执行(无论成功还是异常)
*/
@After("execution(* com.example.service.*.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("【After】方法执行后");
}
/**
* 返回通知(AfterReturning)
* 在目标方法成功返回后执行
*/
@AfterReturning(
pointcut = "execution(* com.example.service.*.*(..))",
returning = "result"
)
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("【AfterReturning】方法返回:" + result);
}
/**
* 异常通知(AfterThrowing)
* 在目标方法抛出异常后执行
*/
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex"
)
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("【AfterThrowing】方法异常:" + ex.getMessage());
}
/**
* 环绕通知(Around)
* 最强大的通知,可以控制目标方法是否执行
*/
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("【Around】方法执行前");
long startTime = System.currentTimeMillis();
try {
// 执行目标方法
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("【Around】方法执行后,耗时:" + (endTime - startTime) + "ms");
return result;
} catch (Throwable ex) {
System.out.println("【Around】方法异常:" + ex.getMessage());
throw ex;
}
}
}
执行顺序
正常流程:
┌────────────────────────────────────┐
│ Around - Before │
│ ┌──────────────────────────────┐ │
│ │ Before │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ 目标方法执行 │ │ │
│ │ └────────────────────────┘ │ │
│ │ AfterReturning │ │
│ └──────────────────────────────┘ │
│ Around - After │
└────────────────────────────────────┘
After
异常流程:
┌────────────────────────────────────┐
│ Around - Before │
│ ┌──────────────────────────────┐ │
│ │ Before │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ 目标方法抛出异常 │ │ │
│ │ └────────────────────────┘ │ │
│ │ AfterThrowing │ │
│ └──────────────────────────────┘ │
│ Around - Exception │
└────────────────────────────────────┘
After
🎯 JDK动态代理
原理
JDK动态代理:
- 基于接口
- 使用java.lang.reflect.Proxy
- 目标类必须实现接口
- 生成接口的实现类
原理图:
┌──────────────┐
│ Interface │ ← 定义接口
└──────┬───────┘
│
┌──────▼───────┐
│ 目标类实现 │ ← 实现接口
└──────────────┘
│
│ Spring生成代理
↓
┌──────────────┐
│ 代理类实现 │ ← 也实现接口
└──────────────┘
调用链:
Client → Proxy → InvocationHandler → 目标方法
代码实现
/**
* 接口
*/
public interface UserService {
void addUser(String name);
void deleteUser(Long id);
}
/**
* 目标类(实现接口)
*/
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
@Override
public void deleteUser(Long id) {
System.out.println("删除用户:" + id);
}
}
/**
* 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("【后置】方法执行后");
return result;
}
}
/**
* 创建代理
*/
public class JdkProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserServiceImpl();
// 2. 创建InvocationHandler
InvocationHandler handler = new LogInvocationHandler(target);
// 3. 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口列表
handler // InvocationHandler
);
// 4. 使用代理对象
proxy.addUser("张三");
// 输出:
// 【前置】方法执行前:addUser
// 添加用户:张三
// 【后置】方法执行后
}
}
JDK动态代理的限制
限制1:必须有接口 ❌
// 没有接口的类
public class OrderService {
public void createOrder() {
System.out.println("创建订单");
}
}
// JDK动态代理无法代理!❌
限制2:只能代理接口方法 ❌
public interface UserService {
void publicMethod(); // 可以代理 ✅
}
public class UserServiceImpl implements UserService {
@Override
public void publicMethod() { }
public void nonInterfaceMethod() { } // 无法代理 ❌
}
🎯 CGLIB代理
原理
CGLIB(Code Generation Library):
- 基于继承
- 使用字节码技术
- 不需要接口
- 生成目标类的子类
原理图:
┌──────────────┐
│ 目标类 │
└──────┬───────┘
│
│ CGLIB生成子类
↓
┌──────────────┐
│ 代理子类 │ ← 继承目标类
└──────────────┘
调用链:
Client → Proxy子类 → MethodInterceptor → 目标方法
代码实现
/**
* 目标类(不需要接口)
*/
public class OrderService {
public void createOrder(Long userId) {
System.out.println("创建订单:userId=" + userId);
}
public void cancelOrder(Long orderId) {
System.out.println("取消订单:orderId=" + orderId);
}
}
/**
* 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("【后置】方法执行后");
return result;
}
}
/**
* 创建代理
*/
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建Enhancer
Enhancer enhancer = new Enhancer();
// 2. 设置父类(目标类)
enhancer.setSuperclass(OrderService.class);
// 3. 设置回调
enhancer.setCallback(new LogMethodInterceptor());
// 4. 创建代理对象
OrderService proxy = (OrderService) enhancer.create();
// 5. 使用代理对象
proxy.createOrder(1001L);
// 输出:
// 【前置】方法执行前:createOrder
// 创建订单:userId=1001
// 【后置】方法执行后
}
}
CGLIB的限制
限制1:无法代理final类 ❌
public final class FinalService {
public void method() { }
}
// CGLIB无法继承final类,代理失败!❌
限制2:无法代理final方法 ❌
public class UserService {
public final void finalMethod() { } // 无法代理 ❌
public void normalMethod() { } // 可以代理 ✅
}
限制3:无法代理private方法 ❌
public class UserService {
private void privateMethod() { } // 无法代理 ❌
public void publicMethod() { } // 可以代理 ✅
}
🆚 JDK vs CGLIB 对比
对比表
| 维度 | JDK动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 接口实现 | 子类继承 |
| 要求 | 必须有接口 | 不需要接口 |
| 性能 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 限制 | 只能代理接口方法 | 不能代理final类/方法 |
| 字节码操作 | 不需要 | 需要(依赖CGLIB库) |
| 代理对象 | 实现接口 | 继承目标类 |
| Spring默认 | 有接口时使用 | 无接口时使用 |
性能对比
JDK 1.6之前:
CGLIB性能更好 ⭐⭐⭐⭐
JDK 1.6-1.8:
JDK动态代理优化,性能相当 ⭐⭐⭐⭐
JDK 1.8之后:
JDK动态代理更快 ⭐⭐⭐⭐⭐
结论:
现在JDK动态代理性能更好
🎯 Spring AOP的选择逻辑
选择规则
/**
* Spring AOP代理选择逻辑
*
* 源码位置:DefaultAopProxyFactory.createAopProxy()
*/
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 条件1:配置强制使用CGLIB
if (config.isOptimize() ||
config.isProxyTargetClass() || // @EnableAspectJAutoProxy(proxyTargetClass = true)
hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class");
}
// 条件2:目标类是接口,使用JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 条件3:使用CGLIB
return new ObjenesisCglibAopProxy(config);
}
else {
// 条件4:有接口,使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
决策树
Spring AOP代理选择:
开始
│
┌─────────▼─────────┐
│ 有接口吗? │
└─────┬────┬────────┘
│ │
有 ◀─┘ └─▶ 没有
│ │
↓ ↓
┌─────────────┐ ┌────────────┐
│强制CGLIB? │ │ 使用CGLIB │
└──┬────┬─────┘ └────────────┘
│ │
是◀─┘ └─▶否
│ │
↓ ↓
┌────────┐ ┌────────────┐
│ CGLIB │ │ JDK动态代理│
└────────┘ └────────────┘
强制使用CGLIB
/**
* 配置强制使用CGLIB
*/
// 方式1:@EnableAspectJAutoProxy注解
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制CGLIB
public class AppConfig {
}
// 方式2:XML配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
// 方式3:SpringBoot配置
spring.aop.proxy-target-class=true
示例
/**
* 示例1:有接口,默认JDK动态代理
*/
public interface UserService {
void addUser(String name);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
// Spring生成的代理:JDK动态代理 ✅
// 原因:有接口
/**
* 示例2:无接口,自动使用CGLIB
*/
@Service
public class OrderService {
public void createOrder() {
System.out.println("创建订单");
}
}
// Spring生成的代理:CGLIB ✅
// 原因:没有接口
/**
* 示例3:有接口 + 强制CGLIB
*/
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
public class AppConfig {
}
public interface ProductService {
void addProduct(String name);
}
@Service
public class ProductServiceImpl implements ProductService {
@Override
public void addProduct(String name) {
System.out.println("添加商品:" + name);
}
}
// Spring生成的代理:CGLIB ✅
// 原因:配置强制使用CGLIB
💻 手写AOP
简化版实现
/**
* 简化版AOP框架
*/
public class SimpleAopFramework {
/**
* 创建代理(自动选择JDK或CGLIB)
*/
public static <T> T createProxy(T target, MethodInterceptor interceptor) {
Class<?> targetClass = target.getClass();
// 检查是否有接口
Class<?>[] interfaces = targetClass.getInterfaces();
if (interfaces.length > 0) {
// 有接口,使用JDK动态代理
return createJdkProxy(target, interfaces, interceptor);
} else {
// 无接口,使用CGLIB
return createCglibProxy(target, interceptor);
}
}
/**
* JDK动态代理
*/
@SuppressWarnings("unchecked")
private static <T> T createJdkProxy(T target, Class<?>[] interfaces,
MethodInterceptor interceptor) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
interfaces,
(proxy, method, args) -> interceptor.invoke(target, method, args)
);
}
/**
* CGLIB代理
*/
@SuppressWarnings("unchecked")
private static <T> T createCglibProxy(T target, MethodInterceptor interceptor) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new net.sf.cglib.proxy.MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return interceptor.invoke(target, method, args);
}
});
return (T) enhancer.create();
}
}
/**
* 方法拦截器接口
*/
@FunctionalInterface
public interface MethodInterceptor {
Object invoke(Object target, Method method, Object[] args) throws Throwable;
}
/**
* 测试
*/
public class AopTest {
public static void main(String[] args) {
// 测试1:有接口(JDK动态代理)
UserService userService = new UserServiceImpl();
UserService proxy1 = SimpleAopFramework.createProxy(userService,
(target, method, args) -> {
System.out.println("【JDK代理】方法执行前:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("【JDK代理】方法执行后");
return result;
});
proxy1.addUser("张三");
System.out.println("代理类型:" + proxy1.getClass().getName());
// 输出:com.sun.proxy.$Proxy0(JDK动态代理)
// 测试2:无接口(CGLIB)
OrderService orderService = new OrderService();
OrderService proxy2 = SimpleAopFramework.createProxy(orderService,
(target, method, args) -> {
System.out.println("【CGLIB代理】方法执行前:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("【CGLIB代理】方法执行后");
return result;
});
proxy2.createOrder();
System.out.println("代理类型:" + proxy2.getClass().getName());
// 输出:OrderService$$EnhancerByCGLIB$$xxx(CGLIB代理)
}
}
🎉 总结
核心要点
1. AOP实现方式
- JDK动态代理:基于接口
- CGLIB:基于继承
2. Spring选择逻辑
- 有接口:JDK动态代理
- 无接口:CGLIB
- 强制CGLIB:配置proxyTargetClass=true
3. 性能
- JDK 1.8+:JDK动态代理更快
- 实际差异不大
4. 使用建议
- 推荐定义接口(解耦 + JDK代理)
- 无接口场景:CGLIB自动使用
- 不用纠结性能,差异很小
记忆口诀
Spring AOP有两种,
JDK代理和CGLIB。
JDK动态基于接口,
Proxy生成实现类。
InvocationHandler拦截,
invoke方法做代理。
CGLIB基于继承,
Enhancer生成子类。
MethodInterceptor拦截,
intercept来代理。
Spring选择有规则,
有接口用JDK,
没接口用CGLIB,
强制CGLIB可配置。
JDK代理性能好,
字节码不用生成。
CGLIB功能强,
不需接口也能用。
final类和方法,
CGLIB无法代理。
private方法也不行,
继承访问不到。
推荐定义接口,
代码解耦更清晰。
JDK代理是首选,
性能更好没问题!
愿你的代码面向切面,关注点完美分离! 🎭✨