文章目录
-
-
- [特征一:出现了核心的 API "老三样"](#特征一:出现了核心的 API “老三样”)
-
- [🧱 典型的 JDK 动态代理代码长这样:](#🧱 典型的 JDK 动态代理代码长这样:)
- [特征二:出现了 `Enhancer` 和 `MethodInterceptor`(CGLIB 动态代理)](#特征二:出现了
Enhancer和MethodInterceptor(CGLIB 动态代理)) - 特征三:打印对象类名时,带有特殊的"魔法后缀"
- [特征四:经典的"方法拦截"与 AOP(面向切面编程)](#特征四:经典的“方法拦截”与 AOP(面向切面编程))
- [总结:反射 VS 动态代理](#总结:反射 VS 动态代理)
-
要判断一段代码是不是用到了动态代理(Dynamic Proxy),核心的特征可以用一句话来概括:
💡 你手头明明没有这个接口的实现类(没写过实现类的代码),但程序在运行的时候,却凭空产生了一个"神秘的代理对象",不仅能帮你执行方法,还能在方法前后偷偷"加料"(比如加日志、加事务)。
动态代理根据实现技术不同,分为两种:JDK 动态代理 和 CGLIB 动态代理。只要你看到代码里出现以下核心特征,那就是用到了动态代理:
特征一:出现了核心的 API "老三样"
如果是用 JDK 动态代理(基于接口),你的代码里百分之百会包含以下三个固定班底:
InvocationHandler接口 :这是代理对象的"灵魂"。所有被代理的方法被调用时,都会被塞进它的invoke()方法里处理。Proxy.newProxyInstance()方法:这是动态代理的"生产线"。调用它,就会在内存中凭空造出一个代理对象。Method.invoke():在拦截完之后,用来真正执行原本的方法(这里通常会结合反射)。
🧱 典型的 JDK 动态代理代码长这样:
java
// 1. 实现 InvocationHandler 接口
public class MyLogHandler implements InvocationHandler {
private Object target; // 被代理的真实对象
public MyLogHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【日志】方法开始执行了..."); // 👈 关键特征:前置增强(加料)
Object result = method.invoke(target, args); // 执行原方法
System.out.println("【日志】方法执行结束了..."); // 👈 关键特征:后置增强(加料)
return result;
}
}
// 2. 在业务中用 Proxy 凭空凭造代理对象
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[] { UserService.class }, // 必须提供接口
new MyLogHandler(new UserServiceImpl())
);
// 3. 调用代理对象
userServiceProxy.createUser();
特征二:出现了 Enhancer 和 MethodInterceptor(CGLIB 动态代理)
如果你的类没有实现任何接口 ,JDK 代理就失效了。这时候框架会转而使用 CGLIB (基于继承)。
如果代码中出现了以下类,说明使用的是 CGLIB 动态代理:
Enhancer:CGLIB 的动态类生成器。MethodInterceptor:方法拦截器,里面的intercept()方法等同于上面的invoke()。
特征三:打印对象类名时,带有特殊的"魔法后缀"
如果你在调试程序时,把一个对象 System.out.println(obj.getClass().getName()) 打印出来,它的类名不是你熟悉的 com.walissh.service.UserServiceImpl,而是变成了类似下面这种怪异的字符串:
- JDK 代理的特征类名 :
com.sun.proxy.$Proxy24(带有$Proxy字样) - CGLIB 代理的特征类名 :
com.walissh.service.UserServiceImpl$$EnhancerBySpringCGLIB$$b3f27a4e(带有$$EnhancerBy...字样)
只要看到类名里带了这些后缀,说明这个对象根本不是你写出来的,而是框架在内存中动态织入生成的代理类。
特征四:经典的"方法拦截"与 AOP(面向切面编程)
在写 Spring 业务代码时,动态代理最核心的特征就是 AOP(切面)。
比如你在方法上加了一个 @Transactional(声明式事务):
java
@Transactional
public void transferMoney() {
// 你的业务代码:转账 100 块
}
你明明只写了"转账 100 块"的代码,为什么程序能自动帮你开启事务、提交事务、甚至在报错时自动回滚事务?
- 幕后黑手就是动态代理 :Spring 在启动时,用动态代理在内存中悄悄把你的类包装了一下,生成了一个代理类。当别人调用
transferMoney()时,实际上调用的是代理类。代理类先帮你执行connection.setAutoCommit(false)(开启事务),然后再去调用你写的转账代码,最后帮你commit()。
总结:反射 VS 动态代理
- 反射(Reflection) 是:原本有 这个类,但我写代码时不知道它是谁,我在运行时动态地去探查和调用它。
- 动态代理(Dynamic Proxy) 是:原本没有 这个实现类,我通过规则在运行时让 JVM 凭空造一个出来,用来在不修改原代码的前提下,给方法神不知鬼不觉地"包装、加料"。