Skip to content

Commit 681c386

Browse files
committed
docs: add spring startup roadmap and debug app
1 parent 942fbf3 commit 681c386

File tree

13 files changed

+633
-0
lines changed

13 files changed

+633
-0
lines changed

roadmap.md

Lines changed: 146 additions & 0 deletions
Large diffs are not rendered by default.

spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,26 @@ private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
171171
Object target = null;
172172

173173
try {
174+
// --- 第一部分:特殊方法的快速通道处理 ---
175+
// 在执行 AOP 增强逻辑之前,先处理掉一些不需要 AOP 的通用方法,以提高性能。
176+
177+
// 1. 如果调用的是 equals() 方法,并且目标类自己没实现它,就直接用代理的 equals 逻辑。
174178
if (!this.cache.equalsDefined && AopUtils.isEqualsMethod(method)) {
175179
// The target does not implement the equals(Object) method itself.
176180
return equals(args[0]);
177181
}
182+
// 2. 如果调用的是 hashCode() 方法,并且目标类自己没实现它,就直接用代理的 hashCode 逻辑。
178183
else if (!this.cache.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
179184
// The target does not implement the hashCode() method itself.
180185
return hashCode();
181186
}
187+
// 3. 处理 Spring 内部的 DecoratingProxy 接口方法。
182188
else if (method.getDeclaringClass() == DecoratingProxy.class) {
183189
// There is only getDecoratedClass() declared -> dispatch to proxy config.
184190
return AopProxyUtils.ultimateTargetClass(this.advised);
185191
}
192+
// 4. 如果调用的是 Advised 接口中的方法(用于获取 AOP 配置信息),则直接在代理配置上执行并返回。
193+
// 这允许我们检查一个代理对象自身的 AOP 配置。
186194
else if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
187195
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
188196
// Service invocations on ProxyConfig with the proxy config...
@@ -191,39 +199,63 @@ else if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
191199

192200
Object retVal;
193201

202+
// --- 第二部分:准备工作与获取“调用链” ---
203+
204+
// 5. 如果配置了要暴露代理对象(expose-proxy=true)
194205
if (this.advised.isExposeProxy()) {
195206
// Make invocation available if necessary.
207+
// 将当前代理对象放入一个 ThreadLocal 中,这样在目标方法内部就可以通过 AopContext.currentProxy() 拿到它。
208+
// 这解决了在方法内部调用自身其他方法时,无法触发 AOP 的问题。
196209
oldProxy = AopContext.setCurrentProxy(proxy);
197210
setProxyContext = true;
198211
}
199212

200213
// Get as late as possible to minimize the time we "own" the target,
201214
// in case it comes from a pool.
215+
// 6. 从 TargetSource 获取真正的目标对象。延迟获取,以减少对池化对象的占用时间。
202216
target = targetSource.getTarget();
203217
Class<?> targetClass = (target != null ? target.getClass() : null);
204218

205219
// Get the interception chain for this method.
220+
// 7. 【核心步骤】获取将要应用于此方法的“拦截器链”。
221+
// Spring 会根据 AOP 配置(例如 Pointcut 表达式)找到所有匹配的通知(Advice),
222+
// 并将它们组成一个执行链(List)。
206223
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
207224

225+
226+
// --- 第三部分:执行调用链与目标方法 ---
227+
208228
// Check whether we have any advice. If we don't, we can fall back on direct
209229
// reflective invocation of the target, and avoid creating a MethodInvocation.
230+
// 8. 如果拦截器链为空,说明没有任何通知需要应用到此方法上。
210231
if (chain.isEmpty()) {
211232
// We can skip creating a MethodInvocation: just invoke the target directly
212233
// Note that the final invoker must be an InvokerInterceptor so we know it does
213234
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
235+
// 这是一个优化:直接通过反射调用目标对象的原始方法,避免创建 MethodInvocation 对象的开销。
214236
@Nullable Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
215237
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
216238
}
217239
else {
218240
// We need to create a method invocation...
241+
// 9. 【核心步骤】如果拦截器链不为空,就需要执行 AOP 逻辑。
242+
// 创建一个 MethodInvocation 对象,它封装了本次调用的所有信息(代理、目标、方法、参数、调用链等)。
219243
MethodInvocation invocation =
220244
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
221245
// Proceed to the joinpoint through the interceptor chain.
246+
// 【启动调用链】调用 proceed() 方法,这会像多米诺骨牌一样,
247+
// 从第一个拦截器开始,依次执行(例如 @Around -> @Before -> 目标方法 -> @After -> @Around),
248+
// 最后返回目标方法的执行结果。
222249
retVal = invocation.proceed();
223250
}
224251

252+
// --- 第四部分:返回值处理 ---
253+
225254
// Massage return value if necessary.
255+
// 10. 对返回值进行一些特殊处理。
226256
Class<?> returnType = method.getReturnType();
257+
// 如果目标方法返回了 `this`(即目标对象自身),并且方法的返回类型与代理类型兼容,
258+
// 那么应该返回代理对象 `proxy` 而不是原始的 `target`,以确保调用者始终持有的是代理。
227259
if (retVal != null && retVal == target &&
228260
returnType != Object.class && returnType.isInstance(proxy) &&
229261
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
@@ -232,21 +264,28 @@ else if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
232264
// a reference to itself in another returned object.
233265
retVal = proxy;
234266
}
267+
// 如果方法要求返回基本类型,但结果是 null,则抛出异常。
235268
else if (retVal == null && returnType != void.class && returnType.isPrimitive()) {
236269
throw new AopInvocationException(
237270
"Null return value from advice does not match primitive return type for: " + method);
238271
}
272+
// 对 Kotlin 协程的特殊处理
239273
if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) {
240274
return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?
241275
CoroutinesUtils.asFlow(retVal) : CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]);
242276
}
243277
return retVal;
244278
}
245279
finally {
280+
// --- 第五部分:清理工作 ---
281+
282+
// 11. 如果目标对象不是静态的(例如,来自对象池),则释放它。
246283
if (target != null && !targetSource.isStatic()) {
247284
// Must have come from TargetSource.
248285
targetSource.releaseTarget(target);
249286
}
287+
288+
// 12. 如果之前设置了 AopContext,现在必须将其从 ThreadLocal 中清除,以避免内存泄漏。
250289
if (setProxyContext) {
251290
// Restore old proxy.
252291
AopContext.setCurrentProxy(oldProxy);

spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,28 +154,52 @@ public void setArguments(@Nullable Object... arguments) {
154154
@Override
155155
public @Nullable Object proceed() throws Throwable {
156156
// We start with an index of -1 and increment early.
157+
// 1. 判断是否已执行完所有拦截器 (递归的终止条件)
158+
// this.interceptorsAndDynamicMethodMatchers 是一个包含所有拦截器的列表
159+
// currentInterceptorIndex 的初始值为-1
160+
// 如果当前拦截器的索引等于拦截器列表的最后一个索引,说明所有拦截器都执行完了
157161
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
162+
// 调用原始的目标方法(例如,你自己的业务Service方法)
158163
return invokeJoinpoint();
159164
}
160165

166+
// 2. 获取下一个要执行的拦截器
167+
// 使用 ++this.currentInterceptorIndex,先将索引加1,再从列表中获取元素
168+
// 所以第一次调用时,索引从-1变为0,获取第一个拦截器
161169
Object interceptorOrInterceptionAdvice =
162170
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
171+
172+
// 3. 判断拦截器的类型
163173
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher dm) {
164174
// Evaluate dynamic method matcher here: static part will already have
165175
// been evaluated and found to match.
176+
// 3.1 类型一:动态匹配的拦截器
177+
// 这种拦截器的切点(Pointcut)不仅需要匹配方法签名(静态匹配),
178+
// 还需要在运行时根据方法的【参数】来决定是否执行(动态匹配)。
179+
// Spring在构建拦截器链时,静态匹配的部分已经完成了。
166180
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
181+
182+
// 在这里进行动态匹配的检查,检查传入的参数是否满足条件
167183
if (dm.matcher().matches(this.method, targetClass, this.arguments)) {
184+
// 动态匹配成功,则执行该拦截器的 invoke 方法
185+
// 拦截器的 invoke 方法内部会再次调用 proceed(),从而形成调用链
168186
return dm.interceptor().invoke(this);
169187
}
170188
else {
171189
// Dynamic matching failed.
172190
// Skip this interceptor and invoke the next in the chain.
191+
// 动态匹配失败,则跳过当前这个拦截器
192+
// 直接递归调用 proceed(),去执行下一个拦截器
173193
return proceed();
174194
}
175195
}
176196
else {
177197
// It's an interceptor, so we just invoke it: The pointcut will have
178198
// been evaluated statically before this object was constructed.
199+
// 3.2 类型二:静态匹配的拦截器
200+
// 这是最常见的类型。它的切点在编译期或启动时就能确定,无需在运行时检查参数。
201+
// 直接执行该拦截器的 invoke 方法。
202+
// 同样,拦截器的 invoke 方法内部会再次调用 proceed(),将控制权交给下一个拦截器。
179203
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
180204
}
181205
}

spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,49 @@ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, Str
285285
@Override
286286
public @Nullable Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
287287
if (bean != null) {
288+
// 1. 生成一个 key,通常就是 beanName
288289
Object cacheKey = getCacheKey(bean.getClass(), beanName);
290+
/*
291+
* 2. --- 核心判断逻辑 ---
292+
* `earlyBeanReferences` 是一个临时缓存,它记录了所有为了解决循环依赖问题而
293+
* 被提前暴露的 Bean 的引用。这行代码是整个方法的分水岭
294+
*/
289295
if (this.earlyBeanReferences.remove(cacheKey) != bean) {
296+
/*
297+
* 进入此 if 块,意味着以下两种情况之一:
298+
*
299+
* 情况 A:这是一个“普通 Bean”,没有参与循环依赖。
300+
* - 那么 `earlyBeanReferences` 缓存中就没有它的记录
301+
* - `remove(cacheKey)` 会返回 `null`
302+
* - 判断条件变为 `null != bean`,结果为 `true`
303+
* - 流程:进入标准的代理创建流程 `wrapIfNecessary`
304+
*
305+
* 情况 B:这是一个“参与了循环依赖的 Bean”,并且在提前暴露时就已经创建了代理
306+
* - 在 `getEarlyBeanReference` 阶段,一个代理对象被创建并放入了 `earlyBeanReferences` 缓存
307+
* - `remove(cacheKey)` 会返回那个【代理对象】
308+
* - `bean` 参数是当前初始化完成的【原始对象】
309+
* - 判断条件变为 `代理对象 != 原始对象`,结果为 `true`
310+
* - 流程:同样进入 `wrapIfNecessary`。这个方法足够智能,会发现代理已创建,并确保返回正确的代理实例
311+
*/
312+
313+
// `wrapIfNecessary` 会检查所有已知的切面(Aspects),判断当前 bean 是否是其目标
314+
// - 如果是,并且代理还未创建,则创建并返回一个新的代理对象来替换原始 bean
315+
// - 如果不是,则直接返回原始的 bean 对象
290316
return wrapIfNecessary(bean, beanName, cacheKey);
291317
}
292318
}
319+
/*
320+
* 3. 如果代码执行到这里,说明上面的 `if` 条件不满足 (`false`)
321+
* 这只对应一种非常特殊且重要的情况:
322+
*
323+
* 情况 C:这是一个“参与了循环依赖的 Bean”,但在提前暴露时【没有】为它创建代理
324+
* - 在 `getEarlyBeanReference` 阶段,Spring 判断它不需要提前代理,于是将【原始对象】本身放入了 `earlyBeanReferences` 缓存
325+
* - `remove(cacheKey)` 会返回那个【原始对象】
326+
* - `bean` 参数也是这个【原始对象】
327+
* - 判断条件变为 `原始对象 != 原始对象`,结果为 `false`
328+
* - 流程:为了保证在整个容器中该 Bean 引用的绝对一致性(因为依赖它的其他 Bean 已经注入了原始对象),
329+
* Spring 决定在此阶段也【不再】为它创建代理,直接返回原始的 bean 实例
330+
*/
293331
return bean;
294332
}
295333

@@ -323,27 +361,50 @@ protected Object getCacheKey(Class<?> beanClass, @Nullable String beanName) {
323361
* @return a proxy wrapping the bean, or the raw bean instance as-is
324362
*/
325363
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
364+
// --- 第一部分:快速失败与跳过检查 ---
365+
// 1. 如果这个 bean 已经被一个 TargetSource 处理,说明它有特殊的代理逻辑,这里就不再处理
326366
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
327367
return bean;
328368
}
369+
// 2. 检查缓存:如果之前已经判断过这个 bean 不需要被代理,就直接返回
370+
// advisedBeans 是一个缓存 Map,避免对同一个 Bean 重复进行 costly 的 AOP 匹配
329371
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
330372
return bean;
331373
}
374+
// 3. 检查是否是“基础设施类”或应该被“跳过”
375+
// isInfrastructureClass: AOP 框架自身的组件(如 Advisor, Pointcut)不应该被自己代理,否则会无限循环
376+
// shouldSkip: 更通用的跳过逻辑,例如,避免代理 AOP 已经处理过的 Bean
332377
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
333378
this.advisedBeans.put(cacheKey, Boolean.FALSE);
334379
return bean;
335380
}
336381

337382
// Create proxy if we have advice.
383+
// --- 第二部分:核心代理创建逻辑 ---
384+
// 如果 Bean 通过了所有豁免检查,就要进入真正的“安检扫描”环节
385+
386+
// 1. 【核心】为当前 Bean 查找匹配的通知(Advices)和顾问(Advisors)
387+
// 这是 AOP 最关键的匹配步骤。Spring 会遍历所有已知的切面(@Aspect),
388+
// 用它们的切点(Pointcut)去匹配当前 bean 的类和方法
338389
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
390+
// 2. 判断是否有匹配的通知
391+
// 如果返回的数组不是 DO_NOT_PROXY(一个空对象标记),说明找到了至少一个适用于此 bean 的通知
339392
if (specificInterceptors != DO_NOT_PROXY) {
393+
// 缓存标记,说明此 bean 需要被代理
340394
this.advisedBeans.put(cacheKey, Boolean.TRUE);
395+
// 3. 【执行】创建代理对象
396+
// 这个方法会调用 ProxyFactory,使用 CGLIB 或 JDK 动态代理技术,
397+
// 创建一个包装了原始 bean 实例并织入了相应通知(interceptors)的代理对象
341398
Object proxy = createProxy(
342399
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
400+
// 缓存代理的类型,用于后续的类型匹配
343401
this.proxyTypes.put(cacheKey, proxy.getClass());
402+
// 4. 返回创建好的代理对象。这个代理将替换掉原始的 bean 实例
344403
return proxy;
345404
}
346405

406+
// 如果没有找到任何匹配的通知,说明这个 Bean 不需要被代理
407+
// 缓存这个结果,并返回原始的 bean 实例
347408
this.advisedBeans.put(cacheKey, Boolean.FALSE);
348409
return bean;
349410
}
@@ -444,31 +505,51 @@ private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,
444505
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
445506
Object @Nullable [] specificInterceptors, TargetSource targetSource, boolean classOnly) {
446507

508+
// --- 准备阶段:配置装配工具 (ProxyFactory) ---
509+
447510
if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
511+
// 在 BeanFactory 中暴露目标类的类型,方便其他组件解析(例如,按类型注入时能正确找到)
448512
AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
449513
}
450514

515+
// ProxyFactory 是创建代理的核心工具,就像装机的“工作台”
451516
ProxyFactory proxyFactory = new ProxyFactory();
517+
// 从 AutoProxyCreator 复制通用配置(如 exposeProxy=true 等)到工作台
452518
proxyFactory.copyFrom(this);
519+
// 暂时允许对配置进行修改
453520
proxyFactory.setFrozen(false);
454521

522+
523+
// --- 决策阶段:选择代理策略(用 CGLIB 还是 JDK 动态代理?)---
524+
// 这就像决定是用一个“兼容主板”(JDK代理)还是一个“品牌定制主板”(CGLIB代理)。
525+
526+
// shouldProxyTargetClass 会检查全局配置或 bean 上的 @Scope(proxyMode=...) 等
455527
if (shouldProxyTargetClass(beanClass, beanName)) {
528+
// 强制使用 CGLIB 代理(基于类的代理),它通过创建子类来实现
456529
proxyFactory.setProxyTargetClass(true);
457530
}
458531
else {
532+
// 尝试使用 JDK 动态代理(基于接口的代理)
533+
// 首先,检查 bean 是否暴露了特定的接口
459534
Class<?>[] ifcs = (this.beanFactory instanceof ConfigurableListableBeanFactory clbf ?
460535
AutoProxyUtils.determineExposedInterfaces(clbf, beanName) : null);
461536
if (ifcs != null) {
537+
// 如果有接口,就明确告诉工厂使用 JDK 代理模式
462538
proxyFactory.setProxyTargetClass(false);
539+
// 并将所有接口添加到代理配置中,代理将会实现这些接口
463540
for (Class<?> ifc : ifcs) {
464541
proxyFactory.addInterface(ifc);
465542
}
466543
}
544+
// 如果没有找到明确的接口,或者即使找到了接口但策略仍不确定
467545
if (ifcs != null ? ifcs.length == 0 : !proxyFactory.isProxyTargetClass()) {
546+
// 再次评估 beanClass 本身实现的接口,这是一个备用策略
468547
evaluateProxyInterfaces(beanClass, proxyFactory);
469548
}
470549
}
471550

551+
// 特殊处理:如果目标本身就是 JDK 代理或 Lambda 表达式,为了支持“引入增强”(Introduction),
552+
// 需要将其实现的接口也添加到新的代理中。
472553
if (proxyFactory.isProxyTargetClass()) {
473554
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
474555
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
@@ -479,21 +560,35 @@ private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
479560
}
480561
}
481562

563+
// --- 组装阶段:将 AOP 组件装配到工厂 ---
564+
565+
// 将原始的拦截器(Interceptors)数组转换为标准的 Advisor 数组。
566+
// Advisor = 通知(Advice) + 切点(Pointcut),即“做什么”+“在哪里做”。
482567
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
568+
// 将“增强功能”(如事务、日志 Advisor)安装到工作台上
483569
proxyFactory.addAdvisors(advisors);
570+
// 将“核心部件”(目标 bean 对象)安装到工作台上
484571
proxyFactory.setTargetSource(targetSource);
572+
// 提供一个钩子方法,允许子类对工作台进行最后的定制化修改
485573
customizeProxyFactory(proxyFactory);
486574

575+
// --- 生产阶段:生成最终的代理对象 ---
576+
577+
// 锁定配置,不允许再修改
487578
proxyFactory.setFrozen(isFrozen());
488579
if (advisorsPreFiltered()) {
580+
// 一个优化,告诉工厂 Advisor 已经预先过滤,无需再次检查
489581
proxyFactory.setPreFiltered(true);
490582
}
491583

492584
// Use original ClassLoader if bean class not locally loaded in overriding class loader
585+
// 获取合适的 ClassLoader
493586
ClassLoader classLoader = getProxyClassLoader();
494587
if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
495588
classLoader = smartClassLoader.getOriginalClassLoader();
496589
}
590+
// 调用工厂的 getProxy() 或 getProxyClass() 方法,正式“生产”出代理。
591+
// 工厂会根据之前设置的策略(CGLIB 或 JDK)来创建最终的产品。
497592
return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
498593
}
499594

0 commit comments

Comments
 (0)