系列文章第1篇 | 共3篇
难度:⭐⭐ | 适合人群:想理解AOP底层原理的开发者
💥 开场:一次"灵异"的Bug
时间: 周四下午
地点: 办公室
事件: 代码评审
我: "这是我写的用户服务,加了事务注解..."
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void createUser(User user) {
userDao.save(user);
System.out.println("用户创建成功");
}
public void batchCreate(List<User> users) {
for (User user : users) {
this.createUser(user); // 调用自己的事务方法
}
}
}
哈吉米: "等等,你这个batchCreate有问题。"
我: "啥问题?调用了事务方法啊。" 🤔
哈吉米: "你试试,如果中间某个用户保存失败,前面保存的会回滚吗?"
我: "会啊,有@Transactional呢。"
测试一下:
List<User> users = Arrays.asList(
new User("张三"),
new User("李四"),
new User(""), // 空名字,会抛异常
new User("王五")
);
try {
userService.batchCreate(users);
} catch (Exception e) {
System.out.println("批量创建失败");
}
// 查询数据库
List<User> all = userDao.findAll();
System.out.println("数据库中有:" + all.size() + " 个用户");
结果:
用户创建成功 // 张三
用户创建成功 // 李四
批量创建失败 // 空名字抛异常
数据库中有:2 个用户 // ← 张三和李四没回滚!
我: "卧槽!事务没生效???" 😱
南北绿豆走过来: "你用this.createUser()调用的,当然不生效。"
我: "为什么?" 😰
阿西噶阿西: "因为@Transactional是通过AOP代理实现的,this调用拿到的是原始对象,不是代理对象,代理逻辑就不会执行!"
我: "代理对象?什么意思?" 🤔
哈吉米: "来,我给你讲讲什么是代理..."
🎯 第一问:什么是代理模式?
生活中的代理
南北绿豆: "你买过房吗?"
我: "租过。"
南北绿豆: "你是直接找房东还是找中介?"
我: "找中介。"
南北绿豆: "这就是代理!"
你 → 中介(代理) → 房东(真实对象)
你不直接接触房东
通过中介来租房
中介可以:
- 提前检查你的信用
- 收取中介费
- 办理合同
- 后续维护
这就是代理模式!
代理模式定义
官方定义:
为其他对象提供一种代理以控制对这个对象的访问。
人话版本:
真实对象(被代理对象)
↓
代理对象(中间人)
↓
客户端(调用方)
客户端不直接访问真实对象,
而是通过代理对象访问,
代理可以在调用前后做额外操作。
静态代理实现
场景: 给UserService的方法加日志
接口:
/**
* 用户服务接口
*/
public interface UserService {
void createUser(String name);
void deleteUser(Long id);
}
真实实现:
/**
* 真实的UserService实现
*/
public class UserServiceImpl implements UserService {
@Override
public void createUser(String name) {
System.out.println("创建用户:" + name);
}
@Override
public void deleteUser(Long id) {
System.out.println("删除用户:" + id);
}
}
代理类:
/**
* UserService的代理类(静态代理)
*/
public class UserServiceProxy implements UserService {
private UserService target; // 真实对象
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void createUser(String name) {
System.out.println(">>> 【日志】开始创建用户:" + name);
long start = System.currentTimeMillis();
target.createUser(name); // 调用真实对象的方法
long end = System.currentTimeMillis();
System.out.println("<<< 【日志】创建完成,耗时:" + (end - start) + "ms");
}
@Override
public void deleteUser(Long id) {
System.out.println(">>> 【日志】开始删除用户:" + id);
long start = System.currentTimeMillis();
target.deleteUser(id); // 调用真实对象的方法
long end = System.currentTimeMillis();
System.out.println("<<< 【日志】删除完成,耗时:" + (end - start) + "ms");
}
}
使用:
public class StaticProxyTest {
public static void main(String[] args) {
// 创建真实对象
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = new UserServiceProxy(userService);
// 通过代理调用
proxy.createUser("张三");
proxy.deleteUser(1L);
}
}
输出:
>>> 【日志】开始创建用户:张三
创建用户:张三
<<< 【日志】创建完成,耗时:2ms
>>> 【日志】开始删除用户:1
删除用户:1
<<< 【日志】删除完成,耗时:1ms
成功! ✨
静态代理的缺点
阿西噶阿西: "静态代理有三大缺点!"
缺点1:代码重复
// 如果有10个方法,就要写10遍日志代码
@Override
public void method1() {
System.out.println(">>> 日志开始");
target.method1();
System.out.println("<<< 日志结束");
}
@Override
public void method2() {
System.out.println(">>> 日志开始"); // 重复
target.method2();
System.out.println("<<< 日志结束"); // 重复
}
// ...重复10次
缺点2:类爆炸
// 如果有100个Service,就要写100个代理类
UserServiceProxy
OrderServiceProxy
ProductServiceProxy
PaymentServiceProxy
...
// 100个代理类!
缺点3:不灵活
// 如果接口新增方法,代理类也要改
public interface UserService {
void createUser(String name);
void deleteUser(Long id);
void updateUser(User user); // ← 新增方法
}
// UserServiceProxy也要新增对应方法
@Override
public void updateUser(User user) {
// 又要写一遍日志逻辑...
}
哈吉米: "这时候,动态代理就登场了!"
🎨 第二问:JDK动态代理
核心API
南北绿豆: "JDK提供了动态代理的API。"
// 核心类
java.lang.reflect.Proxy
// 核心接口
java.lang.reflect.InvocationHandler
InvocationHandler接口
public interface InvocationHandler {
/**
* 代理对象的所有方法调用都会进入这里
*
* @param proxy 代理对象
* @param method 被调用的方法
* @param args 方法参数
* @return 方法返回值
*/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
完整实现
1. 定义InvocationHandler:
package com.example.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 日志代理处理器
*/
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());
System.out.println(" 参数:" + java.util.Arrays.toString(args));
long start = System.currentTimeMillis();
// 调用真实对象的方法
Object result = method.invoke(target, args);
// 后置增强
long end = System.currentTimeMillis();
System.out.println("<<< 【日志】方法返回:" + result);
System.out.println(" 耗时:" + (end - start) + "ms\n");
return result;
}
}
2. 创建代理对象:
package com.example.proxy;
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
System.out.println("===== JDK动态代理测试 =====\n");
// 1. 创建真实对象
UserService target = new UserServiceImpl();
// 2. 创建InvocationHandler
LogInvocationHandler handler = new LogInvocationHandler(target);
// 3. 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口列表
handler // InvocationHandler
);
// 4. 通过代理调用
proxy.createUser("张三");
proxy.deleteUser(1L);
// 5. 查看代理对象信息
System.out.println("真实对象类型:" + target.getClass().getName());
System.out.println("代理对象类型:" + proxy.getClass().getName());
System.out.println("是否是代理:" + Proxy.isProxyClass(proxy.getClass()));
}
}
3. 运行结果:
===== JDK动态代理测试 =====
>>> 【日志】调用方法:createUser
参数:[张三]
创建用户:张三
<<< 【日志】方法返回:null
耗时:2ms
>>> 【日志】调用方法:deleteUser
参数:[1]
删除用户:1
<<< 【日志】方法返回:null
耗时:1ms
真实对象类型:com.example.proxy.UserServiceImpl
代理对象类型:com.sun.proxy.$Proxy0
是否是代理:true
看到了吗?代理对象的类型是$Proxy0,动态生成的! ✨
JDK动态代理原理
阿西噶阿西: "JDK动态代理的原理是在运行时动态生成代理类的字节码。"
生成的代理类(反编译后):
// 这是JDK运行时生成的代理类
public final class $Proxy0 extends Proxy implements UserService {
private static Method m3; // createUser方法
private static Method m4; // deleteUser方法
public $Proxy0(InvocationHandler h) {
super(h);
}
// 实现接口方法
public final void createUser(String name) {
try {
// 调用InvocationHandler的invoke方法
super.h.invoke(this, m3, new Object[]{name});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
public final void deleteUser(Long id) {
try {
super.h.invoke(this, m4, new Object[]{id});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
static {
try {
m3 = Class.forName("com.example.proxy.UserService")
.getMethod("createUser", String.class);
m4 = Class.forName("com.example.proxy.UserService")
.getMethod("deleteUser", Long.class);
} catch (Exception e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
看懂了吗?
- 代理类继承
Proxy,实现UserService接口 - 所有方法调用都委托给
InvocationHandler - 这就是为什么所有方法都进入
invoke()方法!
JDK动态代理的限制
哈吉米: "JDK动态代理有一个致命缺点!"
// ✅ 可以:有接口
public interface UserService {
void createUser(String name);
}
public class UserServiceImpl implements UserService {
@Override
public void createUser(String name) {
// ...
}
}
// 可以创建代理 ✅
UserService proxy = (UserService) Proxy.newProxyInstance(...);
// ❌ 不行:没有接口
public class UserService {
public void createUser(String name) {
// ...
}
}
// 无法创建代理 ❌
// JDK动态代理必须基于接口!
阿西噶阿西: "这时候CGLIB就派上用场了!"
🔧 第三问:CGLIB动态代理
什么是CGLIB?
CGLIB = Code Generation Library(代码生成库)
核心原理: 通过继承目标类,生成子类作为代理
UserService(目标类)
↑ 继承
UserService$$EnhancerByCGLIB$$12345678(代理类)
添加依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
完整实现
1. 目标类(无接口):
/**
* 用户服务(没有接口)
*/
public class UserService {
public void createUser(String name) {
System.out.println("创建用户:" + name);
}
public void deleteUser(Long id) {
System.out.println("删除用户:" + id);
}
}
2. MethodInterceptor:
package com.example.proxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB方法拦截器
*/
public class LogMethodInterceptor implements MethodInterceptor {
/**
* 拦截方法调用
*
* @param obj 代理对象
* @param method 被拦截的方法
* @param args 方法参数
* @param proxy 方法代理(用于调用父类方法)
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 前置增强
System.out.println(">>> 【CGLIB日志】调用方法:" + method.getName());
System.out.println(" 参数:" + java.util.Arrays.toString(args));
long start = System.currentTimeMillis();
// 调用父类方法(真实对象的方法)
Object result = proxy.invokeSuper(obj, args);
// 后置增强
long end = System.currentTimeMillis();
System.out.println("<<< 【CGLIB日志】方法返回:" + result);
System.out.println(" 耗时:" + (end - start) + "ms\n");
return result;
}
}
3. 创建代理:
package com.example.proxy;
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
System.out.println("===== CGLIB动态代理测试 =====\n");
// 1. 创建Enhancer
Enhancer enhancer = new Enhancer();
// 2. 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 3. 设置回调(MethodInterceptor)
enhancer.setCallback(new LogMethodInterceptor());
// 4. 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 5. 通过代理调用
proxy.createUser("李四");
proxy.deleteUser(2L);
// 6. 查看类信息
System.out.println("代理对象类型:" + proxy.getClass().getName());
System.out.println("父类类型:" + proxy.getClass().getSuperclass().getName());
}
}
4. 运行结果:
===== CGLIB动态代理测试 =====
>>> 【CGLIB日志】调用方法:createUser
参数:[李四]
创建用户:李四
<<< 【CGLIB日志】方法返回:null
耗时:1ms
>>> 【CGLIB日志】调用方法:deleteUser
参数:[2]
删除用户:2
<<< 【CGLIB日志】方法返回:null
耗时:0ms
代理对象类型:com.example.proxy.UserService$$EnhancerByCGLIB$$a1b2c3d4
父类类型:com.example.proxy.UserService
看到了吗?代理类继承了UserService! ✨
CGLIB原理
哈吉米: "CGLIB通过继承生成子类。"
生成的代理类(简化版):
// CGLIB生成的代理类
public class UserService$$EnhancerByCGLIB$$a1b2c3d4 extends UserService {
private MethodInterceptor callback;
public void createUser(String name) {
// 拦截方法调用
callback.intercept(this,
UserService.class.getMethod("createUser", String.class),
new Object[]{name},
this.CGLIB$createUser$0$Proxy);
}
// CGLIB生成的代理方法
final void CGLIB$createUser$0(String name) {
super.createUser(name); // 调用父类方法
}
}
CGLIB的限制
// ❌ 无法代理final类
public final class UserService {
// CGLIB无法继承final类
}
// ❌ 无法代理final方法
public class UserService {
public final void createUser(String name) {
// CGLIB无法重写final方法
}
}
// ❌ 无法代理private方法
public class UserService {
private void createUser(String name) {
// CGLIB无法重写private方法
}
}
// ❌ 无法代理static方法
public class UserService {
public static void createUser(String name) {
// CGLIB无法重写static方法
}
}
📊 第四问:JDK vs CGLIB全方位对比
对比表格
| 维度 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 实现方式 | 实现接口 | 继承目标类 |
| 要求 | 必须有接口 | 不需要接口 |
| 性能(创建) | 快 ⚡ | 慢 🐢 |
| 性能(调用) | 稍慢 | 快 ⚡ |
| 代理类 | $Proxy0 | |
| 限制 | 必须有接口 | 不能代理final类/方法 |
| 依赖 | JDK自带 | 需要额外依赖 |
| 适用场景 | 基于接口编程 | 没有接口的类 |
性能测试
public class PerformanceTest {
public static void main(String[] args) {
int count = 100000;
// JDK代理测试
long jdkCreateTime = testJdkProxyCreate(count);
long jdkInvokeTime = testJdkProxyInvoke(count);
// CGLIB代理测试
long cglibCreateTime = testCglibProxyCreate(count);
long cglibInvokeTime = testCglibProxyInvoke(count);
System.out.println("===== 性能测试结果(" + count + "次)=====");
System.out.println("JDK代理创建:" + jdkCreateTime + "ms");
System.out.println("JDK代理调用:" + jdkInvokeTime + "ms");
System.out.println("CGLIB代理创建:" + cglibCreateTime + "ms");
System.out.println("CGLIB代理调用:" + cglibInvokeTime + "ms");
}
// 测试方法实现...
}
测试结果(仅供参考):
===== 性能测试结果(100000次)=====
JDK代理创建:150ms
JDK代理调用:280ms
CGLIB代理创建:450ms
CGLIB代理调用:180ms
结论:
- JDK创建快,调用稍慢
- CGLIB创建慢,调用快
🌟 第五问:Spring如何选择代理方式?
默认策略
南北绿豆: "Spring会根据情况自动选择。"
if (目标类有接口) {
使用JDK动态代理 // 默认
} else {
使用CGLIB代理
}
强制使用CGLIB
方式1:配置文件
spring:
aop:
proxy-target-class: true # 强制使用CGLIB
方式2:注解
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
public class AopConfig {
}
验证代理类型
@Service
public class UserService implements IUserService {
@PostConstruct
public void init() {
System.out.println("UserService类型:" + this.getClass().getName());
if (AopUtils.isJdkDynamicProxy(this)) {
System.out.println(">>> 使用的是JDK动态代理");
} else if (AopUtils.isCglibProxy(this)) {
System.out.println(">>> 使用的是CGLIB代理");
}
}
}
输出(有接口):
UserService类型:com.sun.proxy.$Proxy123
>>> 使用的是JDK动态代理
输出(强制CGLIB):
UserService类型:com.example.UserService$$EnhancerBySpringCGLIB$$a1b2c3d4
>>> 使用的是CGLIB代理
💻 第六问:手写简易版动态代理框架
目标
实现一个简单的代理工具类,支持:
- ✅ 自动选择JDK或CGLIB
- ✅ 支持方法拦截
- ✅ 支持before、after增强
实现代码
package com.example.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 简易版代理工厂
*/
public class SimpleProxyFactory {
/**
* 创建代理对象
*
* @param target 目标对象
* @param interceptor 拦截器
* @return 代理对象
*/
public static Object createProxy(Object target, MethodInterceptor interceptor) {
Class<?> targetClass = target.getClass();
// 判断是否有接口
if (targetClass.getInterfaces().length > 0) {
// 有接口,使用JDK动态代理
System.out.println(">>> 使用JDK动态代理");
return createJdkProxy(target, interceptor);
} else {
// 无接口,使用CGLIB
System.out.println(">>> 使用CGLIB代理");
return createCglibProxy(target, interceptor);
}
}
/**
* 创建JDK代理
*/
private static Object createJdkProxy(Object target, MethodInterceptor interceptor) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 调用拦截器
return interceptor.intercept(target, method, args, null);
}
}
);
}
/**
* 创建CGLIB代理
*/
private static Object createCglibProxy(Object 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.intercept(target, method, args, proxy);
}
});
return enhancer.create();
}
/**
* 方法拦截器接口
*/
public interface MethodInterceptor {
Object intercept(Object target, Method method, Object[] args, Object proxy)
throws Throwable;
}
}
测试
public class SimpleProxyTest {
public static void main(String[] args) {
System.out.println("===== 简易版代理框架测试 =====\n");
// 测试1:有接口的类(JDK代理)
System.out.println("--- 测试1:有接口的类 ---");
IUserService userService = new UserServiceImpl();
IUserService userProxy = (IUserService) SimpleProxyFactory.createProxy(
userService,
(target, method, args1, proxy) -> {
System.out.println(">>> Before: " + method.getName());
Object result = method.invoke(target, args1);
System.out.println("<<< After: " + method.getName() + "\n");
return result;
}
);
userProxy.createUser("张三");
// 测试2:没接口的类(CGLIB代理)
System.out.println("\n--- 测试2:没接口的类 ---");
OrderService orderService = new OrderService();
OrderService orderProxy = (OrderService) SimpleProxyFactory.createProxy(
orderService,
(target, method, args1, proxy) -> {
System.out.println(">>> Before: " + method.getName());
Object result = method.invoke(target, args1);
System.out.println("<<< After: " + method.getName() + "\n");
return result;
}
);
orderProxy.createOrder(123L);
}
}
输出:
===== 简易版代理框架测试 =====
--- 测试1:有接口的类 ---
>>> 使用JDK动态代理
>>> Before: createUser
创建用户:张三
<<< After: createUser
--- 测试2:没接口的类 ---
>>> 使用CGLIB代理
>>> Before: createOrder
创建订单:123
<<< After: createOrder
成功!自动选择代理方式! ✨
💡 知识点总结
本篇你学到了什么?
✅ 代理模式
- 为对象提供代理控制访问
- 可以在调用前后增强功能
- 静态代理 vs 动态代理
✅ JDK动态代理
- 基于接口实现
- Proxy.newProxyInstance()
- InvocationHandler
- 运行时生成代理类字节码
✅ CGLIB动态代理
- 基于继承实现
- Enhancer
- MethodInterceptor
- 生成目标类的子类
✅ JDK vs CGLIB
- JDK必须有接口,CGLIB不需要
- JDK创建快,CGLIB调用快
- CGLIB不能代理final类/方法
✅ Spring的选择策略
- 默认:有接口用JDK,无接口用CGLIB
- 可强制使用CGLIB
✅ 手写代理框架
- 自动选择代理方式
- 支持方法拦截
记忆口诀
代理模式做中介,
JDK基于接口来。
CGLIB继承生子类,
运行时刻动态生。
有接口用JDK代理,
无接口CGLIB来帮忙。
Spring自动会选择,
AOP基于代理搞。
🤔 思考题
问题1: 为什么final方法无法被代理?
问题2: 如果目标类既有接口,又有没在接口中定义的方法,会怎样?
问题3: this调用为什么AOP不生效?
提示: 下一篇会详细解答!
📢 下期预告
《Spring AOP原理(二):@Aspect注解深入,5种通知类型完全掌握!》
下一篇我们将:
- 深入Spring AOP核心概念
- 学习@Aspect、@Before、@Around等注解
- 掌握切点表达式
- 理解5种通知的执行顺序
- 实战:自定义日志切面、权限切面
让你从代理进阶到AOP! 🚀
💬 互动时间
你在实际开发中用过动态代理吗?
JDK和CGLIB你更倾向哪个?
还有哪些关于代理的疑问?
欢迎评论区讨论!💭
觉得有帮助?三连支持: 👍 点赞 | ⭐ 收藏 | 🔄 转发
看完这篇,动态代理不再神秘! ✨
下一篇见! 👋