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

文章目录

      • [特征一:出现了核心的 API "老三样"](#特征一:出现了核心的 API “老三样”)
        • [🧱 典型的 JDK 动态代理代码长这样:](#🧱 典型的 JDK 动态代理代码长这样:)
      • [特征二:出现了 `Enhancer` 和 `MethodInterceptor`(CGLIB 动态代理)](#特征二:出现了 Enhancer 和 MethodInterceptor(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(); 

特征二:出现了 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 凭空造一个出来,用来在不修改原代码的前提下,给方法神不知鬼不觉地"包装、加料"。
相关推荐
云和数据.ChenGuang6 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
摇滚侠7 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
silvamux_9 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
步行cgn10 小时前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
摇滚侠10 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
小蒜学长10 小时前
基于Spring Boot+Vue3的在线医疗咨询系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·在线问诊·在线医疗咨询系统
Deng桑10 小时前
SpringAI写第一个Agent-把工单循环跑起来
java·agent·工具调用·spring ai
步行cgn12 小时前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc
jason成都13 小时前
RTKLIB 的 Java 移植版来了:rtklib-java v3.3.0 功能全景
java·gnss·rtklib·rtklib_java
LazyHunter913 小时前
JDK 28:Java 下一步方向解析与新特性展望
java·jdk