Spring 原理总览从启动到请求执行前面出过一篇 Spring Bean 生命周期的原理文章今天再出一篇关于 Spring 启动工作以及一个 http 请求的后端链路原理希望能帮你构建起知识图谱。Spring 的很多能力看起来分布在不同模块里Autowired负责依赖注入Transactional负责声明式事务GetMapping负责请求映射Component、Service负责组件注册WebMvcConfigurer负责扩展 MVC 配置这些能力背后其实都指向同一组底层问题为什么加一个Service对象就能被 Spring 管起来为什么加一个Autowired成员变量就能被自动赋值为什么写一个WebConfig就能影响所有请求为什么Transactional能自动开启、提交、回滚事务为什么请求来了Spring MVC 能准确找到某个 Controller 方法这些问题看起来分散其实背后是同一个答案Spring 是一个“启动期构建元数据运行期按规则执行”的框架。只要抓住这句话IOC、AOP、MVC、事务、拦截器这些知识点就能被放回同一套框架里理解。目录Spring 最核心的职责是什么启动时 Spring 到底在忙什么为什么说 BeanDefinition 是对象说明书请求进来后 Spring MVC 如何找到 ControllerWebConfig 和 Interceptor 为什么会生效Tomcat 在这条链路里负责什么Filter、Interceptor、AOP 到底差在哪Transactional 为什么本质上是代理以后怎么用统一视角学习 SpringSpring 最核心的职责是什么先从最基础的地方开始。如果不用 Spring我们写 Java 对象大概是这样UserServiceuserServicenewUserService();OrderServiceorderServicenewOrderService(userService);对象由你自己创建依赖由你自己传递生命周期也由你自己控制。这在小项目里没什么问题。但项目一复杂对象之间的关系会迅速变成一张网Controller 依赖 ServiceService 依赖 RepositoryRepository 依赖 DataSourceService 之间还可能相互协作有些对象还要加事务、缓存、权限、日志如果所有对象都靠业务代码自己new后面会越来越难维护。Spring 的第一件事就是把这些对象统一接管。比如ServicepublicclassUserService{}你没有手动new UserService()但 Spring 启动后UserService会进入 IOC 容器。可以简单理解成IOC 容器 ├── UserController ├── UserService ├── OrderService ├── UserRepository └── DataSource这些被 Spring 管理的对象就叫 Bean。所以IOC 不是一个很玄的概念。它做的事情很朴素原来由你自己创建和组装对象现在交给 Spring 创建和组装。对象控制权从业务代码转移到容器这就是“控制反转”。启动时 Spring 到底在忙什么Spring Boot 项目通常从这里启动SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}这几行代码背后Spring 做了大量准备工作。简化一下大致可以理解为启动应用 ↓ 扫描类路径 ↓ 发现候选 Bean ↓ 解析注解和配置 ↓ 生成 BeanDefinition ↓ 注册到 IOC 容器 ↓ 实例化 Bean ↓ 完成依赖注入 ↓ 执行初始化逻辑 ↓ 应用准备就绪注意一个关键点Spring 并不是一开始就直接创建所有对象。它会先分析这些类和配置生成一批“对象说明书”。这些说明书就是BeanDefinition。理解 Spring 启动流程时不只要看“对象什么时候创建”还要看Spring 启动阶段先收集元数据再根据元数据构建对象和规则。这也是为什么你写一个注解Spring 就能知道怎么处理。注解本身不会自动工作。真正工作的是 Spring 在启动时扫描、解析、注册、构建出来的一整套规则。为什么说 BeanDefinition 是对象说明书如果 Bean 是对象本身那么BeanDefinition就是创建这个对象之前的说明书。它里面会记录类似这些信息这个 Bean 对应哪个类它是单例还是每次新建它依赖哪些 Bean它有没有初始化方法它有没有销毁方法它有哪些注解信息它是否需要被代理比如你写ServicepublicclassOrderService{privatefinalUserServiceuserService;publicOrderService(UserServiceuserService){this.userServiceuserService;}}Spring 启动时会识别到OrderService是一个需要管理的 Bean它构造方法需要一个UserService容器里应该先能找到UserService创建OrderService时要把UserService注入进去所以Autowired或构造器注入并不是“运行中突然找对象”。更准确地说是 Spring 在容器初始化过程中根据 Bean 的定义、依赖关系和自动装配规则把对象关系提前组装好。这也是为什么 Spring 官方文档会把 Bean 和依赖关系看作配置元数据的一部分。XML、注解、Java Config 只是元数据来源不同最后都会进入容器的统一处理流程。你可以把它理解成注解 / XML / Java Config ↓ 配置元数据 ↓ BeanDefinition ↓ Bean 实例 ↓ 可运行的对象关系这条链路非常重要。因为后面所有“为什么配置会生效”的问题本质都能塞回这条链路里。请求进来后 Spring MVC 如何找到 Controller接下来换到 Web 场景。你写一个接口RestControllerpublicclassUserController{GetMapping(/users/{id})publicUserDTOgetUser(PathVariableLongid){returnuserService.getUser(id);}}表面看是请求/users/1后执行getUser()。但 Spring MVC 不可能每次请求来了才临时全项目搜索哪个方法能处理这个 URL。真正的逻辑也是启动期建表运行期查表。启动时Spring MVC 会扫描 Controller 中的RequestMapping、GetMapping等注解建立 URL 到处理方法的映射关系。可以理解成一张路由表GET /users/{id} - UserController#getUser(Long id) POST /users - UserController#createUser(UserCreateDTO dto) DELETE /users/{id} - UserController#deleteUser(Long id)请求真的进来时就不需要重新分析注解了。运行时只要根据请求路径、HTTP 方法等信息找到对应的处理方法即可。这也是为什么 Spring MVC 的核心链路里会出现这些角色请求 ↓ DispatcherServlet ↓ HandlerMapping ↓ HandlerExecutionChain ↓ HandlerAdapter ↓ Controller 方法 ↓ HttpMessageConverter ↓ 响应简单翻译一下DispatcherServlet统一接收和调度请求HandlerMapping根据请求找到处理器HandlerExecutionChain处理器加上拦截器链HandlerAdapter用合适的方式调用处理器HttpMessageConverter把 Java 对象和 HTTP 请求响应体互相转换所以DispatcherServlet不是拦截器。它更像 Web 层的总调度员也就是经典的前端控制器模式。WebConfig 和 Interceptor 为什么会生效再看一个很常见的问题。你写了一个配置类ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(loginInterceptor);}}为什么这几行代码能影响所有请求很多人会误以为是不是每个请求进来时都会执行一次addInterceptors()不是。它真正发生在启动阶段。大致过程是Spring MVC 初始化 ↓ 找到所有 WebMvcConfigurer ↓ 执行 addInterceptors() ↓ 把拦截器注册到 InterceptorRegistry ↓ 保存到 HandlerMapping 相关结构中所以registry.addInterceptor(loginInterceptor)的本质不是在处理某个请求而是在注册一条 MVC 规则。等请求进来时Spring MVC 已经知道这个 Controller 调用前后需要经过哪些 Interceptor于是一次请求可能变成请求 ↓ LoginInterceptor.preHandle() ↓ TraceInterceptor.preHandle() ↓ Controller ↓ TraceInterceptor.postHandle() ↓ LoginInterceptor.postHandle() ↓ afterCompletion()这就是 Interceptor 的本质它不是 AOP 代理而是 Spring MVC 请求处理链上的责任链。它包装的是 Controller 调用前后的 Web 请求流程。Tomcat 在这条链路里负责什么前面讲请求链路时我们会看到浏览器 ↓ Tomcat ↓ Filter ↓ DispatcherServlet ↓ Controller这里很容易产生一个疑问既然已经有了 Filter、DispatcherServlet、Controller为什么上面还需要一个 Tomcat答案是Tomcat 负责把“网络请求”变成“Java Web 程序可以处理的 Servlet 请求”。Filter 和 DispatcherServlet 都不是直接监听端口的。它们不会自己去做这些事情监听 8080 端口接收 TCP 连接解析 HTTP 报文维护请求线程创建HttpServletRequest创建HttpServletResponse按 Servlet 规范调用 Filter 和 Servlet管理 Web 应用的生命周期这些事情是 Tomcat 这类 Servlet 容器负责的。可以把层级拆开看操作系统网络层 ↓ Tomcat Connector ↓ HTTP 请求解析 ↓ Servlet 容器 ↓ FilterChain ↓ DispatcherServlet ↓ Spring MVC ↓ ControllerSpring MVC 的入口是DispatcherServlet但DispatcherServlet本身也是一个 Servlet。既然是 Servlet它就需要运行在 Servlet 容器里。Tomcat 做的事情就是提供这个运行环境。更具体一点Tomcat ├── 负责监听端口 ├── 负责接收 HTTP 请求 ├── 负责解析请求报文 ├── 负责创建 Request / Response 对象 ├── 负责管理线程池 ├── 负责执行 Filter 链 └── 负责调用目标 Servlet而 Spring MVC 接手的是 Tomcat 之后的事情DispatcherServlet ├── 根据请求查找 HandlerMapping ├── 找到 Controller 方法 ├── 执行 Interceptor ├── 调用 HandlerAdapter ├── 执行业务 Controller └── 通过 HttpMessageConverter 写回响应所以 Tomcat 和 Spring MVC 的分工可以这样理解Tomcat把 HTTP 请求带进 Java 世界 Spring MVC把 Java Web 请求分发到业务方法没有 Tomcat 会怎么样如果没有 Tomcat普通 Spring MVC 应用就没有 Servlet 运行环境。也就是说没有人监听端口没有人接收浏览器请求没有人解析 HTTP没有人创建HttpServletRequest没有人执行 Filter没有人调用DispatcherServlet这时你的 Controller 写得再完整也不会被浏览器请求触发。因为请求根本进不了 Spring MVC。当然这里说的“没有 Tomcat”不是说一定必须用 Tomcat 这个产品。你也可以用 Jetty、Undertow 等其他 Web 容器。Spring Boot 默认常见的是内嵌 Tomcat所以你平时可能没有显式安装 Tomcat也能启动 Web 项目Spring Boot 应用启动 ↓ 启动内嵌 Tomcat ↓ Tomcat 监听端口 ↓ 注册 DispatcherServlet ↓ 请求进入 Spring MVC这也是 Spring Boot Web 项目能直接java -jar跑起来的原因之一。以前传统部署方式是打成 war 包 ↓ 放进外部 Tomcat ↓ 由外部 Tomcat 启动应用Spring Boot 常见方式是打成 jar 包 ↓ 应用自己带着内嵌 Tomcat ↓ main 方法启动整个 Web 服务但无论是外部 Tomcat还是内嵌 Tomcat职责没有变Tomcat 负责 Servlet 容器层Spring MVC 负责 Web 框架层。所以完整链路应该这样看浏览器发送 HTTP 请求 ↓ Tomcat 接收和解析请求 ↓ Tomcat 创建 Request / Response ↓ Tomcat 执行 FilterChain ↓ Tomcat 调用 DispatcherServlet ↓ Spring MVC 查找 Controller ↓ Controller 执行业务逻辑 ↓ Spring MVC 写回响应 ↓ Tomcat 把响应返回给浏览器一句话总结Tomcat 是 Spring MVC 的运行底座。它负责把 HTTP 世界接进 Java Servlet 世界DispatcherServlet 则负责把 Servlet 请求分发进 Spring MVC 的业务处理流程。Filter、Interceptor、AOP 到底差在哪Filter、Interceptor、AOP 经常被放在一起问。它们都能做“增强”但增强的位置完全不同。Filter 在更外层。浏览器 ↓ Tomcat ↓ Filter ↓ DispatcherServletFilter 属于 Servlet 体系处理的是更原始的 HTTP 请求和响应。它适合做跨域处理编码处理请求压缩安全框架入口全局请求包装Interceptor 在 Spring MVC 内部。DispatcherServlet ↓ Interceptor ↓ Controller它已经进入 Spring MVC 的处理范围能拿到更多 Handler 相关上下文。它适合做登录校验权限判断Controller 日志接口耗时统计请求链路追踪AOP 则不关心 HTTP。调用方 ↓ 代理对象 ↓ 目标 Bean 方法AOP 包装的是 Spring Bean 的方法调用。它适合做事务缓存重试异步方法级日志业务切面所以三者的区别不要只背“执行顺序”。更关键的是看它们包装的对象Filter 包装 HTTP 请求响应 Interceptor 包装 Controller 调用链 AOP 包装 Bean 方法调用一旦抓住这个区别就不会把 Interceptor 和 AOP 混在一起。Transactional 为什么本质上是代理最后看Transactional。你写TransactionalpublicvoidcreateOrder(){orderRepository.save(order);}从业务代码看你只是加了一个注解。但 Spring 启动时会识别事务元数据然后为相关 Bean 创建代理对象。运行时调用的不是原始对象而是代理对象调用 createOrder() ↓ 进入事务代理 ↓ 开启事务 ↓ 执行目标方法 ↓ 根据结果提交或回滚Spring 官方文档里对声明式事务的解释也非常明确它依赖 AOP 代理事务通知由注解或 XML 这类元数据驱动并通过TransactionInterceptor配合事务管理器完成方法调用前后的事务处理。这也是为什么事务有一些经典失效场景同一个类内部方法互相调用可能绕过代理非 public 方法在代理模式下可能不符合预期异常被 catch 后没有继续抛出事务感知不到失败默认情况下受检异常不一定触发回滚新线程里的逻辑不自动继承当前线程事务这些不是“玄学失效”而是代理模型带来的边界。你只要记住一句话Transactional生效的关键不是注解本身而是调用有没有经过 Spring 创建的事务代理。这比单独背十几个事务失效场景更有用。以后怎么用统一视角学习 Spring到这里你会发现 Spring 很多能力都能用同一套问题来理解启动时收集了什么元数据 启动时构建了什么对象关系 启动时注册了什么执行规则 运行时谁来查这些规则 运行时谁来真正执行比如Autowired启动时分析依赖关系 ↓ 容器创建 Bean 时完成注入 ↓ 运行时直接使用已经组装好的对象GetMapping启动时扫描 Controller 方法 ↓ 建立请求路径到方法的映射 ↓ 运行时 HandlerMapping 查找并交给 HandlerAdapter 调用WebConfig启动时执行 MVC 配置回调 ↓ 把拦截器、格式化器、消息转换器等注册进 MVC 体系 ↓ 运行时按已注册规则处理请求Transactional启动时识别事务元数据 ↓ 创建 AOP 代理 ↓ 运行时代理在方法前后控制事务所以学 Spring不要只问“这个注解怎么用”。更应该问这个注解在启动期被谁扫描变成了什么元数据注册到了哪里运行期由谁使用这个问题一问出来很多零散知识点就会自动连成一条线。最后总结Spring 不是一个“帮你写业务代码”的框架。它真正厉害的地方是把对象管理、依赖关系、请求路由、事务、AOP、MVC 配置这些能力都放进了一套统一机制里启动期扫描元数据 ↓ 构建对象关系 ↓ 构建执行规则 ↓ 运行期按规则调度执行IOC、AOP、MVC、事务、Interceptor、Spring Security本质上都是这套思想在不同场景下的具体实现。当你能用这套视角看 Spring再去学循环依赖、三级缓存、自动配置、事务传播、Security 过滤器链就不会觉得它们是完全割裂的知识点。它们只是同一个框架思想在不同层面的展开。如果你以前看 Spring 总觉得概念很多可以先不用急着展开更多分支。先把这句话记住Spring 的核心机制是启动期把注解和配置变成规则运行期按规则执行。参考资料Spring Framework Reference: IoC ContainerSpring Framework Reference: Web MVC DispatcherServlet and HandlerMappingSpring Framework Reference: Spring AOP ProxiesSpring Framework Reference: Declarative Transaction Management
Spring 原理总览:从启动到请求执行
Spring 原理总览从启动到请求执行前面出过一篇 Spring Bean 生命周期的原理文章今天再出一篇关于 Spring 启动工作以及一个 http 请求的后端链路原理希望能帮你构建起知识图谱。Spring 的很多能力看起来分布在不同模块里Autowired负责依赖注入Transactional负责声明式事务GetMapping负责请求映射Component、Service负责组件注册WebMvcConfigurer负责扩展 MVC 配置这些能力背后其实都指向同一组底层问题为什么加一个Service对象就能被 Spring 管起来为什么加一个Autowired成员变量就能被自动赋值为什么写一个WebConfig就能影响所有请求为什么Transactional能自动开启、提交、回滚事务为什么请求来了Spring MVC 能准确找到某个 Controller 方法这些问题看起来分散其实背后是同一个答案Spring 是一个“启动期构建元数据运行期按规则执行”的框架。只要抓住这句话IOC、AOP、MVC、事务、拦截器这些知识点就能被放回同一套框架里理解。目录Spring 最核心的职责是什么启动时 Spring 到底在忙什么为什么说 BeanDefinition 是对象说明书请求进来后 Spring MVC 如何找到 ControllerWebConfig 和 Interceptor 为什么会生效Tomcat 在这条链路里负责什么Filter、Interceptor、AOP 到底差在哪Transactional 为什么本质上是代理以后怎么用统一视角学习 SpringSpring 最核心的职责是什么先从最基础的地方开始。如果不用 Spring我们写 Java 对象大概是这样UserServiceuserServicenewUserService();OrderServiceorderServicenewOrderService(userService);对象由你自己创建依赖由你自己传递生命周期也由你自己控制。这在小项目里没什么问题。但项目一复杂对象之间的关系会迅速变成一张网Controller 依赖 ServiceService 依赖 RepositoryRepository 依赖 DataSourceService 之间还可能相互协作有些对象还要加事务、缓存、权限、日志如果所有对象都靠业务代码自己new后面会越来越难维护。Spring 的第一件事就是把这些对象统一接管。比如ServicepublicclassUserService{}你没有手动new UserService()但 Spring 启动后UserService会进入 IOC 容器。可以简单理解成IOC 容器 ├── UserController ├── UserService ├── OrderService ├── UserRepository └── DataSource这些被 Spring 管理的对象就叫 Bean。所以IOC 不是一个很玄的概念。它做的事情很朴素原来由你自己创建和组装对象现在交给 Spring 创建和组装。对象控制权从业务代码转移到容器这就是“控制反转”。启动时 Spring 到底在忙什么Spring Boot 项目通常从这里启动SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}这几行代码背后Spring 做了大量准备工作。简化一下大致可以理解为启动应用 ↓ 扫描类路径 ↓ 发现候选 Bean ↓ 解析注解和配置 ↓ 生成 BeanDefinition ↓ 注册到 IOC 容器 ↓ 实例化 Bean ↓ 完成依赖注入 ↓ 执行初始化逻辑 ↓ 应用准备就绪注意一个关键点Spring 并不是一开始就直接创建所有对象。它会先分析这些类和配置生成一批“对象说明书”。这些说明书就是BeanDefinition。理解 Spring 启动流程时不只要看“对象什么时候创建”还要看Spring 启动阶段先收集元数据再根据元数据构建对象和规则。这也是为什么你写一个注解Spring 就能知道怎么处理。注解本身不会自动工作。真正工作的是 Spring 在启动时扫描、解析、注册、构建出来的一整套规则。为什么说 BeanDefinition 是对象说明书如果 Bean 是对象本身那么BeanDefinition就是创建这个对象之前的说明书。它里面会记录类似这些信息这个 Bean 对应哪个类它是单例还是每次新建它依赖哪些 Bean它有没有初始化方法它有没有销毁方法它有哪些注解信息它是否需要被代理比如你写ServicepublicclassOrderService{privatefinalUserServiceuserService;publicOrderService(UserServiceuserService){this.userServiceuserService;}}Spring 启动时会识别到OrderService是一个需要管理的 Bean它构造方法需要一个UserService容器里应该先能找到UserService创建OrderService时要把UserService注入进去所以Autowired或构造器注入并不是“运行中突然找对象”。更准确地说是 Spring 在容器初始化过程中根据 Bean 的定义、依赖关系和自动装配规则把对象关系提前组装好。这也是为什么 Spring 官方文档会把 Bean 和依赖关系看作配置元数据的一部分。XML、注解、Java Config 只是元数据来源不同最后都会进入容器的统一处理流程。你可以把它理解成注解 / XML / Java Config ↓ 配置元数据 ↓ BeanDefinition ↓ Bean 实例 ↓ 可运行的对象关系这条链路非常重要。因为后面所有“为什么配置会生效”的问题本质都能塞回这条链路里。请求进来后 Spring MVC 如何找到 Controller接下来换到 Web 场景。你写一个接口RestControllerpublicclassUserController{GetMapping(/users/{id})publicUserDTOgetUser(PathVariableLongid){returnuserService.getUser(id);}}表面看是请求/users/1后执行getUser()。但 Spring MVC 不可能每次请求来了才临时全项目搜索哪个方法能处理这个 URL。真正的逻辑也是启动期建表运行期查表。启动时Spring MVC 会扫描 Controller 中的RequestMapping、GetMapping等注解建立 URL 到处理方法的映射关系。可以理解成一张路由表GET /users/{id} - UserController#getUser(Long id) POST /users - UserController#createUser(UserCreateDTO dto) DELETE /users/{id} - UserController#deleteUser(Long id)请求真的进来时就不需要重新分析注解了。运行时只要根据请求路径、HTTP 方法等信息找到对应的处理方法即可。这也是为什么 Spring MVC 的核心链路里会出现这些角色请求 ↓ DispatcherServlet ↓ HandlerMapping ↓ HandlerExecutionChain ↓ HandlerAdapter ↓ Controller 方法 ↓ HttpMessageConverter ↓ 响应简单翻译一下DispatcherServlet统一接收和调度请求HandlerMapping根据请求找到处理器HandlerExecutionChain处理器加上拦截器链HandlerAdapter用合适的方式调用处理器HttpMessageConverter把 Java 对象和 HTTP 请求响应体互相转换所以DispatcherServlet不是拦截器。它更像 Web 层的总调度员也就是经典的前端控制器模式。WebConfig 和 Interceptor 为什么会生效再看一个很常见的问题。你写了一个配置类ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(loginInterceptor);}}为什么这几行代码能影响所有请求很多人会误以为是不是每个请求进来时都会执行一次addInterceptors()不是。它真正发生在启动阶段。大致过程是Spring MVC 初始化 ↓ 找到所有 WebMvcConfigurer ↓ 执行 addInterceptors() ↓ 把拦截器注册到 InterceptorRegistry ↓ 保存到 HandlerMapping 相关结构中所以registry.addInterceptor(loginInterceptor)的本质不是在处理某个请求而是在注册一条 MVC 规则。等请求进来时Spring MVC 已经知道这个 Controller 调用前后需要经过哪些 Interceptor于是一次请求可能变成请求 ↓ LoginInterceptor.preHandle() ↓ TraceInterceptor.preHandle() ↓ Controller ↓ TraceInterceptor.postHandle() ↓ LoginInterceptor.postHandle() ↓ afterCompletion()这就是 Interceptor 的本质它不是 AOP 代理而是 Spring MVC 请求处理链上的责任链。它包装的是 Controller 调用前后的 Web 请求流程。Tomcat 在这条链路里负责什么前面讲请求链路时我们会看到浏览器 ↓ Tomcat ↓ Filter ↓ DispatcherServlet ↓ Controller这里很容易产生一个疑问既然已经有了 Filter、DispatcherServlet、Controller为什么上面还需要一个 Tomcat答案是Tomcat 负责把“网络请求”变成“Java Web 程序可以处理的 Servlet 请求”。Filter 和 DispatcherServlet 都不是直接监听端口的。它们不会自己去做这些事情监听 8080 端口接收 TCP 连接解析 HTTP 报文维护请求线程创建HttpServletRequest创建HttpServletResponse按 Servlet 规范调用 Filter 和 Servlet管理 Web 应用的生命周期这些事情是 Tomcat 这类 Servlet 容器负责的。可以把层级拆开看操作系统网络层 ↓ Tomcat Connector ↓ HTTP 请求解析 ↓ Servlet 容器 ↓ FilterChain ↓ DispatcherServlet ↓ Spring MVC ↓ ControllerSpring MVC 的入口是DispatcherServlet但DispatcherServlet本身也是一个 Servlet。既然是 Servlet它就需要运行在 Servlet 容器里。Tomcat 做的事情就是提供这个运行环境。更具体一点Tomcat ├── 负责监听端口 ├── 负责接收 HTTP 请求 ├── 负责解析请求报文 ├── 负责创建 Request / Response 对象 ├── 负责管理线程池 ├── 负责执行 Filter 链 └── 负责调用目标 Servlet而 Spring MVC 接手的是 Tomcat 之后的事情DispatcherServlet ├── 根据请求查找 HandlerMapping ├── 找到 Controller 方法 ├── 执行 Interceptor ├── 调用 HandlerAdapter ├── 执行业务 Controller └── 通过 HttpMessageConverter 写回响应所以 Tomcat 和 Spring MVC 的分工可以这样理解Tomcat把 HTTP 请求带进 Java 世界 Spring MVC把 Java Web 请求分发到业务方法没有 Tomcat 会怎么样如果没有 Tomcat普通 Spring MVC 应用就没有 Servlet 运行环境。也就是说没有人监听端口没有人接收浏览器请求没有人解析 HTTP没有人创建HttpServletRequest没有人执行 Filter没有人调用DispatcherServlet这时你的 Controller 写得再完整也不会被浏览器请求触发。因为请求根本进不了 Spring MVC。当然这里说的“没有 Tomcat”不是说一定必须用 Tomcat 这个产品。你也可以用 Jetty、Undertow 等其他 Web 容器。Spring Boot 默认常见的是内嵌 Tomcat所以你平时可能没有显式安装 Tomcat也能启动 Web 项目Spring Boot 应用启动 ↓ 启动内嵌 Tomcat ↓ Tomcat 监听端口 ↓ 注册 DispatcherServlet ↓ 请求进入 Spring MVC这也是 Spring Boot Web 项目能直接java -jar跑起来的原因之一。以前传统部署方式是打成 war 包 ↓ 放进外部 Tomcat ↓ 由外部 Tomcat 启动应用Spring Boot 常见方式是打成 jar 包 ↓ 应用自己带着内嵌 Tomcat ↓ main 方法启动整个 Web 服务但无论是外部 Tomcat还是内嵌 Tomcat职责没有变Tomcat 负责 Servlet 容器层Spring MVC 负责 Web 框架层。所以完整链路应该这样看浏览器发送 HTTP 请求 ↓ Tomcat 接收和解析请求 ↓ Tomcat 创建 Request / Response ↓ Tomcat 执行 FilterChain ↓ Tomcat 调用 DispatcherServlet ↓ Spring MVC 查找 Controller ↓ Controller 执行业务逻辑 ↓ Spring MVC 写回响应 ↓ Tomcat 把响应返回给浏览器一句话总结Tomcat 是 Spring MVC 的运行底座。它负责把 HTTP 世界接进 Java Servlet 世界DispatcherServlet 则负责把 Servlet 请求分发进 Spring MVC 的业务处理流程。Filter、Interceptor、AOP 到底差在哪Filter、Interceptor、AOP 经常被放在一起问。它们都能做“增强”但增强的位置完全不同。Filter 在更外层。浏览器 ↓ Tomcat ↓ Filter ↓ DispatcherServletFilter 属于 Servlet 体系处理的是更原始的 HTTP 请求和响应。它适合做跨域处理编码处理请求压缩安全框架入口全局请求包装Interceptor 在 Spring MVC 内部。DispatcherServlet ↓ Interceptor ↓ Controller它已经进入 Spring MVC 的处理范围能拿到更多 Handler 相关上下文。它适合做登录校验权限判断Controller 日志接口耗时统计请求链路追踪AOP 则不关心 HTTP。调用方 ↓ 代理对象 ↓ 目标 Bean 方法AOP 包装的是 Spring Bean 的方法调用。它适合做事务缓存重试异步方法级日志业务切面所以三者的区别不要只背“执行顺序”。更关键的是看它们包装的对象Filter 包装 HTTP 请求响应 Interceptor 包装 Controller 调用链 AOP 包装 Bean 方法调用一旦抓住这个区别就不会把 Interceptor 和 AOP 混在一起。Transactional 为什么本质上是代理最后看Transactional。你写TransactionalpublicvoidcreateOrder(){orderRepository.save(order);}从业务代码看你只是加了一个注解。但 Spring 启动时会识别事务元数据然后为相关 Bean 创建代理对象。运行时调用的不是原始对象而是代理对象调用 createOrder() ↓ 进入事务代理 ↓ 开启事务 ↓ 执行目标方法 ↓ 根据结果提交或回滚Spring 官方文档里对声明式事务的解释也非常明确它依赖 AOP 代理事务通知由注解或 XML 这类元数据驱动并通过TransactionInterceptor配合事务管理器完成方法调用前后的事务处理。这也是为什么事务有一些经典失效场景同一个类内部方法互相调用可能绕过代理非 public 方法在代理模式下可能不符合预期异常被 catch 后没有继续抛出事务感知不到失败默认情况下受检异常不一定触发回滚新线程里的逻辑不自动继承当前线程事务这些不是“玄学失效”而是代理模型带来的边界。你只要记住一句话Transactional生效的关键不是注解本身而是调用有没有经过 Spring 创建的事务代理。这比单独背十几个事务失效场景更有用。以后怎么用统一视角学习 Spring到这里你会发现 Spring 很多能力都能用同一套问题来理解启动时收集了什么元数据 启动时构建了什么对象关系 启动时注册了什么执行规则 运行时谁来查这些规则 运行时谁来真正执行比如Autowired启动时分析依赖关系 ↓ 容器创建 Bean 时完成注入 ↓ 运行时直接使用已经组装好的对象GetMapping启动时扫描 Controller 方法 ↓ 建立请求路径到方法的映射 ↓ 运行时 HandlerMapping 查找并交给 HandlerAdapter 调用WebConfig启动时执行 MVC 配置回调 ↓ 把拦截器、格式化器、消息转换器等注册进 MVC 体系 ↓ 运行时按已注册规则处理请求Transactional启动时识别事务元数据 ↓ 创建 AOP 代理 ↓ 运行时代理在方法前后控制事务所以学 Spring不要只问“这个注解怎么用”。更应该问这个注解在启动期被谁扫描变成了什么元数据注册到了哪里运行期由谁使用这个问题一问出来很多零散知识点就会自动连成一条线。最后总结Spring 不是一个“帮你写业务代码”的框架。它真正厉害的地方是把对象管理、依赖关系、请求路由、事务、AOP、MVC 配置这些能力都放进了一套统一机制里启动期扫描元数据 ↓ 构建对象关系 ↓ 构建执行规则 ↓ 运行期按规则调度执行IOC、AOP、MVC、事务、Interceptor、Spring Security本质上都是这套思想在不同场景下的具体实现。当你能用这套视角看 Spring再去学循环依赖、三级缓存、自动配置、事务传播、Security 过滤器链就不会觉得它们是完全割裂的知识点。它们只是同一个框架思想在不同层面的展开。如果你以前看 Spring 总觉得概念很多可以先不用急着展开更多分支。先把这句话记住Spring 的核心机制是启动期把注解和配置变成规则运行期按规则执行。参考资料Spring Framework Reference: IoC ContainerSpring Framework Reference: Web MVC DispatcherServlet and HandlerMappingSpring Framework Reference: Spring AOP ProxiesSpring Framework Reference: Declarative Transaction Management