外观
一句话答案
事务失效常见原因:自调用(未走代理)、方法非 public、异常被 catch 吞掉、rollbackFor 未指定、非 Spring 管理的 Bean。
核心要点
SpringApplication.run() 的完整流程:
SpringApplication.run(MyApp.class, args)
│
▼
① 创建 SpringApplication 对象
→ 推断应用类型(NONE/SERVLET/REACTIVE)
→ 从 spring.factories 加载 ApplicationContextInitializer
→ 从 spring.factories 加载 ApplicationListener
▼
② run() 方法执行
│
├─ 获取 SpringApplicationRunListeners(从 spring.factories 加载)
│ 发布 ApplicationStartingEvent
│
├─ 准备环境(prepareEnvironment)
│ → 创建 ConfigurableEnvironment
│ → 加载配置(application.properties / application.yml)
│ → 发布 ApplicationEnvironmentPreparedEvent
│
├─ 打印 Banner(Spring Boot 的 ASCII 图案)
│
├─ 创建 ApplicationContext
│ → 根据应用类型创建 AnnotationConfigServletWebServerApplicationContext
│ → 或 AnnotationConfigApplicationContext(非 Web)
│
├─ prepareContext(准备上下文)
│ → 应用 ApplicationContextInitializer
│ → 注册主配置类(@SpringBootApplication 所在类)
│ → 发布 ApplicationContextInitializedEvent
│
├─ refreshContext(刷新上下文)← 核心步骤,调用 AbstractApplicationContext.refresh()
│ → 加载所有 Bean 定义(BeanDefinition)
│ → 创建并初始化所有单例 Bean(走 Bean 生命周期)
│ → 启动内嵌 Tomcat(onRefresh 阶段)
│ → 发布 ContextRefreshedEvent
│
├─ afterRefresh(刷新后)
│ → 调用所有 ApplicationRunner / CommandLineRunner 的 run() 方法
│
└─ 发布 ApplicationStartedEvent
发布 ApplicationReadyEvent ← 服务完全启动,可对外提供服务关键:refreshContext() 内部调用 AbstractApplicationContext.refresh(),这 12 步是 Spring 容器启动的核心:
prepareRefresh() → 初始化环境变量
obtainFreshBeanFactory() → 创建/刷新 BeanFactory
prepareBeanFactory() → 注册内置组件(如 Environment)
postProcessBeanFactory() → 子类扩展
invokeBeanFactoryPostProcessors() → 执行 BeanFactoryPostProcessor(如扫描 @Component)
registerBeanPostProcessors() → 注册 BeanPostProcessor
initMessageSource() → 国际化
initApplicationEventMulticaster() → 初始化事件广播器
onRefresh() → 子类扩展(Web 容器在这里启动 Tomcat)
registerListeners() → 注册监听器
finishBeanFactoryInitialization() → 实例化所有非懒加载单例 Bean(核心!)
finishRefresh() → 发布 ContextRefreshedEvent追问与易错
追问方向:
- 自调用为什么事务失效?怎么解决?(未经过代理,解决:注入自己/AopContext/抽方法到另一个类)
- @Transactional 默认回滚什么异常?(RuntimeException + Error,不回滚 checked Exception)
- 事务传播行为 REQUIRES_NEW 和 NESTED 的区别?(NEW 独立事务,NESTED 基于保存点)
易错点:
- ❌ "加了 @Transactional 就有事务"——还要检查代理方式、异常类型、是否自调用
- ❌ "private 方法加 @Transactional 也有效"——无效,CGLIB 无法代理 private 方法
💡 记忆锚点
事务失效 = 代理没接住:自调用(this绕过代理)、非public(代理拦不到)、异常被catch(Spring不知道出事了)、checked异常(默认只回滚RuntimeException)。记住"事务靠代理,代理靠public方法的外部调用"。