那用到动态代理,关键的特征又是什么呢

文章目录

      • [特征一:出现了核心的 API "老三样"](#特征一:出现了核心的 API “老三样”)
        • [🧱 典型的 JDK 动态代理代码长这样:](#🧱 典型的 JDK 动态代理代码长这样:)
      • [特征二:出现了 `Enhancer` 和 `MethodInterceptor`(CGLIB 动态代理)](#特征二:出现了 EnhancerMethodInterceptor(CGLIB 动态代理))
      • 特征三:打印对象类名时,带有特殊的"魔法后缀"
      • [特征四:经典的"方法拦截"与 AOP(面向切面编程)](#特征四:经典的“方法拦截”与 AOP(面向切面编程))
      • [总结:反射 VS 动态代理](#总结:反射 VS 动态代理)

要判断一段代码是不是用到了动态代理(Dynamic Proxy),核心的特征可以用一句话来概括:

💡 你手头明明没有这个接口的实现类(没写过实现类的代码),但程序在运行的时候,却凭空产生了一个"神秘的代理对象",不仅能帮你执行方法,还能在方法前后偷偷"加料"(比如加日志、加事务)。

动态代理根据实现技术不同,分为两种:JDK 动态代理CGLIB 动态代理。只要你看到代码里出现以下核心特征,那就是用到了动态代理:


特征一:出现了核心的 API "老三样"

如果是用 JDK 动态代理(基于接口),你的代码里百分之百会包含以下三个固定班底:

  1. InvocationHandler 接口 :这是代理对象的"灵魂"。所有被代理的方法被调用时,都会被塞进它的 invoke() 方法里处理。
  2. Proxy.newProxyInstance() 方法:这是动态代理的"生产线"。调用它,就会在内存中凭空造出一个代理对象。
  3. 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(); 

特征二:出现了 EnhancerMethodInterceptor(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 凭空造一个出来,用来在不修改原代码的前提下,给方法神不知鬼不觉地"包装、加料"。
相关推荐
嘻哈∠※5 小时前
0061基于 SpringBoot 的投稿与稿件处理系统设计与实现
java·spring boot·后端
白狐_7986 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2587 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
码匠许师傅7 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
极创信息9 小时前
系统安全隐患全方位排查方案:标准化渗透测试全流程
java·opencv·struts·数据挖掘·eclipse·语音识别·hibernate
汉字萌萌哒9 小时前
2019CCF-CSP入门级C++试题解析
java·开发语言·c++
喜欢的名字被抢了9 小时前
程序出问题怎么查,以及如何让它不掉线
java·运维·数据库
数据知道10 小时前
反序列化漏洞:Java、PHP、Python 三条线各讲透
java·网络·python·安全·网络安全·php
2601_9499506310 小时前
Java 面试准备,用练题簿把“背过八股”变成“真正会答”
java·开发语言·面试·刷题·小程序推荐
Rain的Java大神之路11 小时前
MQ重复消费问题怎么解决
java·经验分享·后端·面试·架构·rabbitmq·rocketmq