news 2025/12/22 14:39:56

[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅

1. 前言:在AI时代重新拾起源码的温暖

"在AI可以自动生成代码的今天,为什么还要读源码?因为理解原理才能让我们从代码的'使用者'变成'创造者'!"

最近AI的崛起确实让技术圈发生了翻天覆地的变化,博主之前的源码解析栏目也因此沉寂了一段时间。不过,在经历了更多生产问题复盘和真实架构设计的实战后,我愈发觉得:理解底层原理才是应对技术变革的不变法宝。

今天,让我们重新点燃源码解析的热情!随着这两年工作的积累,我对这些基础框架有了更深刻的理解,可以为大家带来更多实际应用中的"避坑指南"。好消息是,今天的代码量很少,相信你喝杯咖啡的时间就能轻松掌握!

代码分支:https://github.com/yihuiaa/little-spring/tree/jdk-dynamic-proxy

2. 总体设计:AOP动态代理的"四重奏"

在开始代码之旅前,让我们先认识今天的"主演阵容":

核心组件总览

«interface»

AopProxy

+getProxy() : Object

JdkDynamicAopProxy

-AdvisedSupport advised

+getProxy() : Object

+invoke(Object, Method, Object[]) : Object

AdvisedSupport

-TargetSource targetSource

-MethodInterceptor methodInterceptor

-MethodMatcher methodMatcher

+getter/setter methods

TargetSource

-Object target

+getTargetClass() : Class[]

+getTarget() : Object

«interface»

MethodInterceptor

+invoke(MethodInvocation) : Object

ReflectiveMethodInvocation

-Object target

-Method method

-Object[] arguments

+proceed() : Object

InvocationHandler

各组件职责说明:

AopProxy:获取代理对象的抽象接口,定义了统一的代理创建标准

JdkDynamicAopProxy:基于JDK动态代理的具体实现,我们的"男主角"

TargetSource:被代理对象的"保镖",负责安全地封装目标对象

MethodInterceptor:方法拦截器,AOP Alliance的"标准公民",可以在方法执行前后插入自定义逻辑

AdvisedSupport:AOP配置的"大脑",协调各个组件协同工作

3. 新增依赖:欢迎AOP Alliance大家庭

在开始编码前,我们需要引入一个重要依赖:

<dependency>

<groupId>aopalliance</groupId>

<artifactId>aopalliance</artifactId>

<version>1.0</version>

</dependency>

这个依赖是什么来头?

AOP Alliance是一个为AOP(面向切面编程)提供标准接口的库,你可以把它想象成AOP世界的"联合国"——它定义了各个AOP框架都能理解的"官方语言",让不同的AOP实现能够和平共处、相互协作。

想象一下,如果没有这个标准,Spring AOP和Guice AOP就像两个说不同语言的人,根本无法交流!

4. 核心代码解析:深入AOP动态代理的内心世界

4.1 AdvisedSupport - AOP配置的"指挥中心"

package org.springframework.aop;

import org.aopalliance.intercept.MethodInterceptor;

/**

* Spring AOP核心配置类 - 负责协调AOP代理的各个组件

* @author yihui

*/

public class AdvisedSupport {

/**

* 目标对象源 - 封装被代理的目标对象

* 就像电影的"选角导演",负责找到合适的演员(目标对象)

*/

private TargetSource targetSource;

/**

* 方法拦截器 - 定义具体的增强逻辑

* 相当于电影的"特效团队",在原有剧情前后添加炫酷特效

*/

private MethodInterceptor methodInterceptor;

/**

* 方法匹配器 - 决定哪些方法需要被拦截

* 就像"剧本编辑",决定哪些场景需要添加特效

*/

private MethodMatcher methodMatcher;

// getter和setter方法...

}

设计亮点:

采用组合模式,将三个核心组件完美整合

配置与执行分离,符合单一职责原则

为后续扩展预留了充足空间

4.2 TargetSource - 目标对象的"贴心保镖"

package org.springframework.aop;

/**

* 被代理的目标对象 - 采用不可变设计确保线程安全

* @author yihui

*/

public class TargetSource {

/**

* 不可变的目标对象引用 - 一旦"签约"就不能更改

*/

private final Object target;

public TargetSource(Object target) {

this.target = target;

}

/**

* 返回目标对象实现的所有接口 - 为JDK动态代理提供"角色清单"

*/

public Class<?>[] getTargetClass() {

return this.target.getClass().getInterfaces();

}

public Object getTarget() {

return this.target;

}

}

为什么需要TargetSource?

想象一下,如果没有这个封装,每次需要目标对象时都要直接操作原始对象,就像没有经纪人的明星——既不够安全,也不够专业!

4.3 JdkDynamicAopProxy - 动态代理的"魔法师"

package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.AdvisedSupport;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/**

* JDK动态代理 - 巧妙融合AOP标准与JDK原生动态代理

* @author yihui

*/

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

private final AdvisedSupport advised;

public JdkDynamicAopProxy(AdvisedSupport advised) {

this.advised = advised;

}

/**

* 创建代理对象 - 这里是魔法开始的地方!

*/

@Override

public Object getProxy() {

return Proxy.newProxyInstance(

getClass().getClassLoader(),

advised.getTargetSource().getTargetClass(),

this

);

}

/**

* 方法调用拦截 - 每个方法调用都要经过这里的"安检"

*/

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 检查这个方法是否需要被拦截(是否需要过安检)

if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {

// 需要拦截:请拦截器来处理(走特殊通道)

MethodInterceptor methodInterceptor = advised.getMethodInterceptor();

return methodInterceptor.invoke(new ReflectiveMethodInvocation(

advised.getTargetSource().getTarget(), method, args));

}

// 不需要拦截:直接放行(走普通通道)

return method.invoke(advised.getTargetSource().getTarget(), args);

}

}

双重身份的魅力:

AopProxy接口:对外提供统一的代理创建接口

InvocationHandler接口:对内处理方法调用的拦截逻辑

这种设计就像一个人既是"建筑设计师"(负责创建),又是"物业经理"(负责运营),确保了整个流程的连贯性。

JDK动态代理 vs CGLIB代理:

特性 JDK动态代理 CGLIB代理

基础 基于接口 基于类继承

依赖 JDK内置,无需额外依赖 需要CGLIB库

性能 JDK6+性能优秀 通常稍慢,但在持续优化

限制 只能代理接口方法 可以代理类,但final方法不行

实际开发中的"坑":

自调用问题:代理对象内部方法互相调用时,不会经过代理

public class UserService {

public void updateUser() {

this.validateUser(); // 这个调用不会走代理!

}

}

equals和hashCode:需要特殊处理,避免代理对象比较时出现意外结果

4.4 ReflectiveMethodInvocation - 方法调用的"时光胶囊"

package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;

import java.lang.reflect.Method;

/**

* 方法调用上下文封装 - 把一次方法调用打包成"标准化包裹"

* @author yihui

*/

public class ReflectiveMethodInvocation implements MethodInvocation {

/**

* 目标对象引用 - 要调用谁

*/

private final Object target;

/**

* 方法元数据 - 要调用什么方法

*/

private final Method method;

/**

* 方法参数 - 调用时传递什么参数

*/

private final Object[] arguments;

public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {

this.target = target;

this.method = method;

this.arguments = arguments;

}

/**

* 执行目标方法 - 打开"时光胶囊",执行原始逻辑

*/

@Override

public Object proceed() throws Throwable {

return method.invoke(target, arguments);

}

// 其他信息获取方法...

@Override

public Method getMethod() { return method; }

@Override

public Object[] getArguments() { return arguments; }

@Override

public Object getThis() { return target; }

@Override

public AccessibleObject getStaticPart() { return method; }

}

为什么需要这个"时光胶囊"?

它把一次方法调用的所有上下文信息完整保存,让拦截器可以在任何时候、任何地方重现这次调用,就像把当下的瞬间封存在胶囊中,随时可以重新开启。

5. 实战测试:让代码"活"起来

理论说再多,不如实际跑一跑!让我们看看这些组件如何协同工作:

public class DynamicProxyTest {

@Test

public void testJdkDynamicProxy() throws Exception {

// 1. 准备目标对象(我们的"演员")

WorldService worldService = new WorldServiceImpl();

// 2. 配置AOP(搭建"拍摄现场")

AdvisedSupport advisedSupport = new AdvisedSupport();

TargetSource targetSource = new TargetSource(worldService);

WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();

MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* service.WorldService.sayHello(..))").getMethodMatcher();

advisedSupport.setTargetSource(targetSource);

advisedSupport.setMethodInterceptor(methodInterceptor);

advisedSupport.setMethodMatcher(methodMatcher);

// 3. 创建代理(开机!)

WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();

// 4. 使用代理(Action!)

proxy.sayHello();

}

}

// 业务接口

public interface WorldService {

void sayHello();

}

// 业务实现

public class WorldServiceImpl implements WorldService {

@Override

public void sayHello() {

System.out.println("Hello World");

}

}

// 自定义拦截器

public class WorldServiceInterceptor implements MethodInterceptor {

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println("方法处理前");

Object result = invocation.proceed();

System.out.println("方法处理后");

return result;

}

}

运行结果:

方法处理前

Hello World

方法处理后

看到这个输出,是不是有种"魔法生效"的成就感?我们的拦截器成功在目标方法执行前后添加了自定义逻辑!

6. 总结:从理解到掌握

通过今天的学习,我们不仅理解了JDK动态代理在Spring AOP中的应用,更重要的是,我们看到了一个优秀框架的设计思想:

标准化思维:通过AOP Alliance接口,确保与生态系统的兼容性

组合优于继承:通过AdvisedSupport组合各个组件,保持灵活性

职责分离:每个类都有明确的单一职责,便于理解和维护

扩展性设计:为后续功能升级预留了充足空间

记住这个精妙的AOP代理流程:

方法调用

匹配

不匹配

代理对象

MethodMatcher检查

MethodInterceptor

直接调用目标方法

ReflectiveMethodInvocation

目标方法执行

创建代理

AdvisedSupport

JdkDynamicAopProxy

代理对象

配置阶段

TargetSource

AdvisedSupport

MethodInterceptor

MethodMatcher

虽然这只是Spring AOP的简化实现,但核心思想与完整版一脉相承。理解了这个基础版本,再去学习完整的Spring AOP源码,就会觉得"原来如此"!

源码阅读就像拼图游戏,一开始可能只见树木不见森林,但当所有碎片就位时,一幅精美的画卷就会呈现在眼前。希望今天的讲解能帮你找到几块关键的拼

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/19 17:07:58

【C++ 笔记】从 C 到 C++:核心过渡 (中)

前言&#xff1a; 前文中&#xff0c;我们系统学习了 namespace 机制&#xff08;有效地解决了命名冲突问题&#xff0c;包含指定访问、部分展开和全部展开三种使用方式&#xff09;&#xff0c;同时了解了 cin/cout 输入输出流&#xff08;具备自动类型识别和支持自定义类…

作者头像 李华
网站建设 2025/12/19 17:07:56

SQL约束解析

约束分类:NOT NULL 非空约束:字段必须有值UNIQUE 唯一约束:值不能重复&#xff0c;但允许多个 NULLPRINARY KEY 主键约束:既是 NOT NULL 又是 UNIQUEDEFAULT 默认约束: 保存数据时.如果未指定该字段的值,则采用默认值CHECK 检查约束:保证字段满足某一个值FOREIGN KEY 外键约束…

作者头像 李华
网站建设 2025/12/19 8:54:10

地铁调研12-17

今天地铁调研主要内容包括&#xff1a;1.跟随工人使用道尺进行巡检。主要测量内容&#xff1a;轨道内距&#xff0c;轨道水平情况。记录&#xff1a;/-x&#xff0c;毫米。2.涂油板&#xff08;道岔变轨部分&#xff09;的油是否还有。3.扣配件的螺栓是否松动扣配件的情况&…

作者头像 李华
网站建设 2025/12/19 8:54:07

现代软件测试工具全景对比与选型指南

随着敏捷开发与DevOps实践的普及&#xff0c;软件测试工具生态呈现百花齐放态势。截至2025年末&#xff0c;测试工具已从简单的BUG记录工具发展为覆盖自动化测试、性能监控、安全检测的完整解决方案。本文将通过功能性对比、适用场景分析及成本效益评估三个维度&#xff0c;为测…

作者头像 李华
网站建设 2025/12/19 17:07:42

基于 Apache POI 的体检报告 Word 生成实战文档

基于 Apache POI 的体检报告 Word 生成实战文档一 项目目标与总体设计 目标&#xff1a;基于模板快速生成排版规范的体检报告&#xff0c;支持文本替换、动态表格、图片插入&#xff0c;并可一键导出 PDF 用于归档与打印。技术选型&#xff1a; Apache POI XWPF&#xff1a;操作…

作者头像 李华
网站建设 2025/12/19 17:07:40

org.jetbrains.annotations的@Nullable 学习

Nullable 是 JetBrains 提供的一套用于 Java 静态分析的注解&#xff08;annotations&#xff09;之一&#xff0c;属于 org.jetbrains.annotations 包。它主要用于标注一个变量、参数、方法返回值等可能为 null&#xff0c;从而帮助 IDE&#xff08;如 IntelliJ IDEA&#xff…

作者头像 李华