0%

背景

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
四层分析模型
第1层:症状层 (Symptom Layer)
   ├─ 用户感知:响应慢、超时、错误
   ├─ 业务指标:QPS下降、成功率降低
   └─ 核心问题:WHAT(发生了什么)
   
第2层:表现层 (Manifestation Layer)
   ├─ 技术指标:GC耗时、CPU使用率、线程数
   ├─ 资源状态:内存占用、连接数、文件描述符
   └─ 核心问题:WHERE(发生在哪个层面)
   
第3层:原因层 (Cause Layer)
   ├─ 直接原因:内存泄漏、锁竞争、外部依赖慢
   ├─ 触发条件:流量高峰、定时任务、数据累积
   └─ 核心问题:WHY(为什么发生)
   
第4层:根因层 (Root Cause Layer)
   ├─ 代码缺陷:资源未释放、算法效率低
   ├─ 设计缺陷:架构不合理、容量规划不足
   ├─ 流程缺陷:缺乏监控、发布不规范
   └─ 核心问题:HOW(如何从根本上避免)

1. 症状层应该观察哪些信息?

系统化的症状观察应覆盖以下五个维度,每个维度回答一个子问题:

Java 线上问题处理操作指南

一、整体排查大纲

1. 总:故障定性与目标

  • 明确故障现象:CPU飙升 + 内存飙升 + 服务不可用(Java实例,Spring微服务)
  • 确定目标优先级:止血(恢复业务)→ 保留现场 → 根因定位 → 永久修复
  • 建立问题分类框架(参考文档中的“排查方向”):
    • 应用程序本身(代码缺陷、JVM参数、资源使用)
    • 上下游系统(流量突增、依赖故障)
    • 机器/容器环境(资源争抢、网络、入侵)
    • 第三方调用(作为被调方流量突增,或作为调方依赖雪崩)

2. 分:按阶段逐步深入

阶段一:快速止血(恢复业务)

  • 决策:摘流 or 重启?保留现场 or 立即恢复?
  • 操作:摘除异常实例,确认业务恢复

阶段二:现场数据收集(证据链构建)

  • 需要什么信息?为什么?
  • 权衡:哪些数据必须取,哪些可能加重故障

阶段三:假设驱动分析(定位根因)

  • 基于文档的常见故障类型,建立假设清单
  • 用多维度数据交叉验证,逐步缩小范围

阶段四:解决与验证

  • 临时措施(重启、调参)与永久修复(代码、配置)
  • 验证问题解决,监控恢复

阶段五:复盘与优化

  • 避免同类问题,提升系统韧性

3. 总:总结核心原则

  • 证据链优于直觉
  • 止血优先于分析
  • 多维度交叉验证
  • 工具是手段,思路是核心

二、详细排查思路与思考过程

阶段一:快速止血(恢复业务)

思考

前言

在软件系统日益复杂的今天,性能问题的排查已成为工程师的核心能力之一。然而,很多人面对问题时,往往陷入“头痛医头、脚痛医脚”的困境——看到GC慢就调GC参数,看到CPU高就加机器,却很少思考问题的本质。

Java 运维诊断实践指南:从零命令依赖到高效问题定位

一、背景与目标

1.1 为什么需要这篇指南

在生产环境中,Java 应用的问题诊断往往面临两难:

  • 公司内网环境:安全合规要求严格,无法直接执行 jstackjcmd 等诊断命令
  • 个人项目环境:可以自由使用命令,但缺乏系统化的诊断方法论

本指南旨在帮助中高级 Java 开发工程师建立一套不依赖命令的运维诊断体系,同时保留个人项目的灵活诊断能力

三大核心日志

MySQL 任意一条写SQL(不包括读SQL)的执行都会记录三个核心日志:undo-log、redo-log、bin-log。

Undo Log(撤销日志)

  • 作用:记录SQL操作的撤销日志,用于事务回滚和MVCC
  • 存储机制
    • 使用共享Buffer Pool中的undo_log_buffer缓冲区
    • 事务写数据前,先将旧数据拷贝到Undo Log。行数据的隐藏字段roll_ptr回滚指针指向Undo Log中的旧数据。
  • 特性
    • 属于"数据页",受Redo Log保护,即写入 Undo Log 也会记录 Redo Log。
    • InnoDB引擎特有

Redo Log(重做日志)

  • 作用:记录数据页的物理变化,用于崩溃恢复
  • 存储机制:记录的是“数据页的物理变化”,例如记录"表空间X、页面Y、偏移Z处的值从A改为B"
  • 刷盘控制
    • 参数innodb_flush_log_at_trx_commit控制刷盘时机
    • 默认值1:每次提交事务时刷盘
  • 特性:InnoDB引擎特有

Bin Log(二进制日志)

  • 作用:记录SQL操作日志,用于主从复制和数据恢复
  • 存储机制:
    • 使用位于线程的工作内存中的缓冲区bin_log_buffer。
    • 追加存储模式,支持保留所有 bin-log。
  • 格式类型
    • STATEMENT:记录SQL语句(默认)
    • ROW:记录行数据变化
    • MIXED:混合模式
  • 刷盘控制
    • 参数sync_binlog控制刷盘时机
    • 默认值1:每次提交事务时刷盘
  • 特性:MySQL Server级别,所有引擎可用

崩溃恢复机制

两阶段提交与崩溃恢复

如果redo-log只写一次,那不管redo-log和 bin-log 谁先写,都有可能造成主从同步数据时的不一致问题出现。

MySQL数据类型隐式转换

隐式转换是 MySQL 在比较或操作不同数据类型时自动进行的类型转换,主要发生在比较运算、算术运算和函数处理等场景。

规则

  1. 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
  2. 两个参数都是字符串,会按照字符串来比较,不做类型转换
  3. 两个参数都是整数,按照整数来比较,不做类型转换
  4. 十六进制的值和非数字做比较时,会被当做二进制串
  5. 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
  6. 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
  7. 所有其他情况下,两个参数都会被转换为浮点数再进行比较

MySQL 的隐式转换虽然方便,但容易导致:

根据 AOP 配置包装 bean

标准创建流程

源码摘录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 构建缓存
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 如果缓存中已存在,则表示已进行提前代理,此处跳过代理。
        // 详情参见循环依赖相关篇章。
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // AOP 代理
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

主流程

1
2
3
4
5
6
7
// 入口方法
--> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
    --> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
        --> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
            --> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 继承父类 postProcessAfterInitialization 方法
                --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
                --|> Bean 创建扩展点方法
1
2
3
4
5
6
7
8
9
// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 继承父类 postProcessAfterInitialization 方法。
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
--> 获取缓存 key
    --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getCacheKey 
--> `this.earlyProxyReferences.remove(cacheKey) != bean`
    --|> 如果缓存中已存在则表示已进行提前代理此处跳过代理
    --|> 详情参见循环依赖相关篇章
    --> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary 
        --|> 参见分支 wrapIfNecessary》。

循环依赖场景中提前创建 AOP 流程

源码摘录

1
2
3
4
5
6
7
8
9
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    // 构建缓存 key
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    // 缓存到变量。在后续该 bean 的标准创建流程中,会根据缓存判断是否已进行提前创建。
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

主流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
--> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
    --> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
        --> for-each smartInstantiationAwares 遍历 SmartInstantiationAwareBeanPostProcessor调用接口方法`SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference`。
            --> AOPorg.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
                --> 构建缓存 key
                    --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getCacheKey
                --> 缓存到变量`AbstractAutoProxyCreator#earlyProxyReferences`
                    --|> `this.earlyProxyReferences.put(cacheKey, bean);`
                    --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#earlyProxyReferences
                    --|> 在后续该 bean 的标准创建流程中会根据缓存判断是否已进行提前创建
                --> AOP 包装
                    --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

AOP 代理逻辑

源码摘录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

主流程

下文分析基于 Spring 注解@Aspect定义的切面。

关键方法列表

  1. 解析 AOP 配置
    1. Spring 扩展点InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法。
    2. org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation
    3. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
  2. 根据 AOP 配置包装 bean
    1. Spring 扩展点InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法。
    2. org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization
    3. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

解析 AOP 配置

AbstractAutoProxyCreator#postProcessBeforeInstantiation

源码摘录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation 继承父类 postProcessBeforeInstantiation 方法。
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

--> 构建缓存 key
    --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getCacheKey
    --> if 存在 beanName 且为 FactoryBean返回`BeanFactory.FACTORY_BEAN_PREFIX + beanName`。
    --> else-if 存在 beanName返回 beanName
    --> else 返回 beanClass
--> 如果 beanName 为空或缓存`AbstractAutoProxyCreator#targetSourcedBeans`中不存在 key
    --> 如果缓存`AbstractAutoProxyCreator#advisedBeans`中已经解析 bean 解析结果则退出方法
--> 匹配`isInfrastructureClass(beanClass)`条件的 bean 不需要 AOP 代理退出方法
    --|> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#isInfrastructureClass
    --|> 功能1Advice, Advisor, Pointcut, AopInfrastructureBean 类型的 Bean 满足条件 isInfrastructureClass不需要被代理
        --|> 底层调用 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#isInfrastructureClass
    --|> 功能2注解 @Aspect 修饰且非 ajc 编译的 bean 满足条件 isAspect进而满足条件 isInfrastructureClass不需要被代理
        --|> 底层调用 org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect
--> 匹配`shouldSkip(beanClass, beanName)`条件的 bean 不需要 AOP 代理
    --|> org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip
    --> 获取所有的 Advisor
        --|> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
        --|> 详情参见分支 findCandidateAdvisors
    --> 如果当前 bean 实现了 AspectJPointcutAdvisor则跳过
        --|> 目前未看到 AspectJPointcutAdvisor 使用场景当前的 advisor 均为 InstantiationModelAwarePointcutAdvisorImpl并未继承 AspectJPointcutAdvisor
        --|>  Advisor 类型的 bean在之前步骤`isInfrastructureClass`中已经过滤了应当不会执行到这里
    --> 判断是否是`OriginalInstance`, bean name 后缀为 .ORIGINAL”,则跳过
        --|> org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#shouldSkip
        --> org.springframework.aop.framework.autoproxy.AutoProxyUtils#isOriginalInstance
--> 如果存在用户自定义的创建代理逻辑 customTargetSourceCreators则使用该工具创建代理默认无

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

源码摘录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
@Override
protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Skipping advisor '" + name +
                                        "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            // Ignore: indicates a reference back to the bean we're trying to advise.
                            // We want to find advisors other than the currently created bean itself.
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }
    return advisors;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // We must be careful not to instantiate beans eagerly as in this case they
                    // would be cached by the Spring container but would not have been weaved.
                    Class<?> beanType = this.beanFactory.getType(beanName, false);
                    if (beanType == null) {
                        continue;
                    }
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                        else {
                            // Per target or per this.
                            if (this.beanFactory.isSingleton(beanName)) {
                                throw new IllegalArgumentException("Bean with name '" + beanName +
                                        "' is a singleton, but aspect instantiation model is not singleton");
                            }
                            MetadataAwareAspectInstanceFactory factory =
                                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                            this.aspectFactoryCache.put(beanName, factory);
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

-->  beanFactory 中获取所有的 Advisor bean
    --|> org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
    -->  beanFactory 中获取所有的 Advisor bean
        --|> this.advisorRetrievalHelper.findAdvisorBeans();
        --|> org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
        --> 如果缓存`BeanFactoryAdvisorRetrievalHelper#cachedAdvisorBeanNames`不为空表示已经完成查询可以则直接使用缓存从第2次查询起可以使用缓存
        --> 第1次查询时不存在缓存需构建 bean name 缓存
            -->  beanFactory 及祖先 beanFactory 中找到所有`Advisor`类型的 bean name存储到缓存中
                --|> BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
                --|> org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class, boolean, boolean)
        --> 遍历上一步骤获取的 bean name如果满足`isEligibleBean`条件则创建 advisor bean
            --> 匹配条件
                --|> isEligibleBean(name)
                --|> org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.BeanFactoryAdvisorRetrievalHelperAdapter#isEligibleBean
                --> AbstractAdvisorAutoProxyCreator.this.isEligibleAdvisorBean(beanName);
                    --|> org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#isEligibleAdvisorBean
                    --|> 始终返回 true
                    --|> return true;
            --> 创建 bean
                --|> this.beanFactory.getBean(name, Advisor.class)
                --|> org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class<T>)
--> 构建 Advisor即获取所有 Spring AOP 配置的切面 Bean
    --|> this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
    --|> org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
    --> 如果缓存`BeanFactoryAspectJAdvisorsBuilder#aspectBeanNames`为空需构建 bean name 缓存第1次查询时不存在缓存
        -->  beanFactory 及祖先 beanFactory 中查询所有的对象方式是找到所有`Object`类型的 bean name
            --|> BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
            --|> org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<?>, boolean, boolean)
        --> 遍历上一步骤获取的 bean name如果满足`isEligibleBean`条件且是 AOP 切面 bean则创建并缓存 advisor
            --> 匹配条件`isEligibleBean(beanName)`,默认始终为 true不满足条件则跳过
                --|> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter#isEligibleBean
                    --> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#isEligibleAspectBean
                    --|> 包含与`<aop:include>`配置相关的匹配条件
                    --|> 当前使用 Spring 注解配置未进行 XML 配置因此满足 isEligibleBean 条件
            --> 获取 beanType如果无法获取则跳过部分 FactoryBean 场景下无法获取
            --> 匹配条件`this.advisorFactory.isAspect(beanType)`,即是否是 AOP 切面不满足条件则跳过
                --|> org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect
                --> 判断 bean 是否被注解`@Aspect`修饰且非 ajc 编译
                    --|> 原因是使用AspectJ语言 Spring AOP编写的切面在通过 ajc 编译得到时也会存在`@Aspect`注解但其并非 Spring 期望的注解方式
                    --> org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#hasAspectAnnotation
                        --> AnnotationUtils.findAnnotation(clazz, Aspect.class) != null
                    --> !compiledByAjc(clazz)
            --> 如果切面 bean  AjType 特性Aspect 注解的 value 值配置是单例封装为`BeanFactoryAspectInstanceFactory`。
                --|> 调用`advisorFactory#getAdvisors` 构造 Advisor
                    --|> org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors
                --|> 如果 bean 是单例则存储到缓存`BeanFactoryAspectJAdvisorsBuilder#advisorsCache`,否则存储到缓存`BeanFactoryAspectJAdvisorsBuilder#aspectFactoryCache`。
            --> 如果切面 bean  AjType 特性非单例 bean 也必须是非单例的否则抛出异常
                --|> 封装为`PrototypeAspectInstanceFactory`,存储到缓存`BeanFactoryAspectJAdvisorsBuilder#aspectFactoryCache`。
                --|> 调用`advisorFactory#getAdvisors`构造 Advisor
        --> 返回所有的 advisor
    --> 如果缓存`BeanFactoryAspectJAdvisorsBuilder#aspectBeanNames`不为空则基于缓存获取或创建 advisor从第2次查询起可以使用缓存
        --> 遍历缓存中的 aspectNames
            --> 如果存在于缓存 BeanFactoryAspectJAdvisorsBuilder#advisorsCache 则直接使用缓存中的 advisor
                --|> 表示单例 advisor
            --> 如果存在于缓存 BeanFactoryAspectJAdvisorsBuilder#aspectFactoryCache 则调用 advisorFactory#getAdvisors 构造 advisor
                --|> 表示非单例 advisor
        --> 返回所有的 advisor

此处的 advisor 有

创建事务-createTransactionIfNecessary

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
--> 如果未主动指定事务名称则使用 joinpointIdentification类名称+方法名)。将事务属性封装为`DelegatingTransactionAttribute`。
--> 创建事务
    --|> tm.getTransaction(txAttr);
    --|> org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction            
    --> org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction
        --> 创建`DataSourceTransactionObject`对象封装数据源与事务信息
        --> 配置属性`savepointAllowed`,值来源于`AbstractPlatformTransactionManager#nestedTransactionAllowed`,`DataSourceTransactionManager`中该值默认为 true
        --> 获取数据源当前使用 jdbc `HikariDataSource`数据源
            --|> `obtainDataSource()`。
            --|> org.springframework.jdbc.datasource.DataSourceTransactionManager#obtainDataSource
                --> org.springframework.jdbc.datasource.DataSourceTransactionManager#getDataSource
        --> 资源绑定以数据源信息作为 key
            --|> org.springframework.transaction.support.TransactionSynchronizationManager#getResource
        --> 配置属性`ConnectionHolder``DataSourceTransactionObject#newConnectionHolder` false
    --> 如果已存在事务则进行嵌套事务处理
        --> 判断方式是查询上一步骤获取的`DataSourceTransactionObject`对象中的`ConnectionHolder`是否已激活事务
            --|> org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction
            --> txObject.getConnectionHolder().isTransactionActive()
        --> 嵌套事务处理
            --|> org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
            --|> 待补充
    --> 校验 timeout 配置
    --> 如果事务属性为`TransactionDefinition.PROPAGATION_MANDATORY`,则抛出异常该属性要求已存在1个事务而如果执行到此处表示不存在事务
    --> 如果事务属性为`TransactionDefinition.PROPAGATION_REQUIRED``TransactionDefinition.PROPAGATION_REQUIRES_NEW``TransactionDefinition.PROPAGATION_NESTED`
        --> 挂起资源执行到此处时不存在事务
        --> 启动事务org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction
            --> 
                --|> org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
            --> 根据需要初始化事务同步
                --|> org.springframework.transaction.support.AbstractPlatformTransactionManager#prepareSynchronization

    --> 否则创建1个空事务
        --> 获取属性`newSynchronization`。
--> 封装事务相关信息为 TransactionInfo并绑定到当前线程
    --|> org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo
    --> 缓存当前线程中的`transactionInfoHolder`到属性`oldTransactionInfo`,设置新的`transactionInfoHolder`。
        ---|> org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread

提交事务-commit