高频考点 + 核心机制图解 — 从原理到生产实战
IoC容器 · Bean生命周期 · AOP · 循环依赖 · 设计模式
IoC(Inversion of Control)控制反转:对象的创建和管理权从程序代码转移到 Spring 容器。控制权被"反转"了。
DI(Dependency Injection)依赖注入:IoC 的具体实现方式,容器自动将依赖对象注入到需要的地方。
二者的关系:IoC 是思想,DI 是实现方式
没有 IoC 的世界:每个类自己 new 依赖对象。OrderService 里写 new UserRepo(),换数据库实现要改源码、重新编译
有 IoC 的世界:所有对象由容器创建和管理。OrderService 只声明"我需要 UserRepo"(@Autowired),容器自动注入实现
核心好处:面向接口编程,依赖关系由容器集中管理,更换实现只需改配置不改代码
| 对比 | BeanFactory | ApplicationContext |
|---|---|---|
| 定位 | IoC 容器最基础接口 | BeanFactory 的超集,高级容器 |
| Bean 加载时机 | 懒加载(Lazy Loading) getBean 时才创建 |
预加载(Eager Loading) 容器启动时就创建所有单例 Bean |
| 国际化支持 | ❌ 不支持 | ✅ MessageSource |
| 事件发布 | ❌ 不支持 | ✅ ApplicationEvent |
| AOP 集成 | 需手动配置 | ✅ 自动集成 |
| 资源访问 | ❌ 不支持 | ✅ ResourceLoader |
| 使用场景 | 资源受限的环境(移动端等) | 绝大多数场景都用这个 ✅ |
| 方式 | 示例 | 推荐度 | 原因 |
|---|---|---|---|
| 构造器注入 | 构造方法上加 @Autowired | ⭐⭐⭐ 首选 | 依赖不可变(final)、保证不为null、完全初始化、方便单元测试 |
| Setter 注入 | setter 方法上加 @Autowired | ⭐⭐ 可选依赖 | 灵活,可重新配置,适合可选依赖 |
| 字段注入 | 字段上直接 @Autowired | ⭐ 不推荐 | 不能声明final、无法脱离IoC容器测试、隐藏依赖关系、Spring官方不推荐 |
| 对比 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring 框架注解 org.springframework.beans.factory.annotation |
JSR-250 标准(Java) javax.annotation |
| 注入顺序 | 先 byType → 再 byName → 还不行就报错 | 先 byName → 再 byType → 还不行就报错 |
| 指定名称 | 配合 @Qualifier("beanName") | @Resource(name="beanName") |
| required 支持 | ✅ @Autowired(required=false) 允许注入null | ❌ 不支持,找不到就报错 |
| 适用范围 | 构造器、方法、字段、参数 | 方法、字段 |
面试必答模板:
1. IoC 是什么?→ 控制反转,对象的创建权从程序转移到容器
2. 以前怎么做?→ 自己 new 对象,对象之间耦合度高
3. 现在怎么做?→ Spring 容器统一管理,需要什么注入什么
4. DI 是什么?→ 依赖注入,是 IoC 的实现方式
5. 好处?→ 解耦、方便测试、提高可维护性
IoC = 不用自己 new,容器管一切
DI = 需要什么,容器注入什么
@Autowired 先类型再名称,@Resource 先名称再类型
Bean 生命周期核心要点:
📋 实例化:反射调用构造方法,对象在堆中分配内存
💉 属性注入:解析 @Autowired/@Value,完成依赖注入
🔔 Aware 回调:把容器相关对象注入 Bean(如 ApplicationContext)
🔧 前置处理:BeanPostProcessor.postProcessBeforeInitialization()
🏁 初始化:@PostConstruct → InitializingBean → init-method
🎭 后置处理:AOP 代理在这一步生成(关键!)
✅ 就绪:放入 singletonObjects,可以被业务使用
🗑️ 销毁:容器关闭时,@PreDestroy → DisposableBean → destroy-method
| 方式 | 初始化 | 销毁 | 执行优先级 |
|---|---|---|---|
| JSR-250 注解 | @PostConstruct | @PreDestroy | 最高 (1) |
| InitializingBean 接口 | afterPropertiesSet() | destroy() | 中间 (2) |
| @Bean 自定义方法 | initMethod="xxx" | destroyMethod="xxx" | 最低 (3) |
面试陷阱:AOP 代理是在 BeanPostProcessor 的后置处理(第6步)中生成的!
这意味着:初始化方法(@PostConstruct)执行时,Bean 还不是代理对象!内部 this 调用不走代理!
面试高频追问:Bean 是线程安全的吗?
→ 不是!Spring Bean 默认 singleton,如果 Bean 有可变状态,多线程并发访问不安全。
解决方案:① 使用 prototype 作用域 ② Bean 设计为无状态 ③ 使用 ThreadLocal
Bean 生命周期记忆口诀:
实例化 → 注入属性 → Aware → 前置 → 初始化 → 后置(AOP) → 使用 → 销毁
关键点:AOP 在后置处理中生成,不是初始化时!
AOP(Aspect-Oriented Programming)面向切面编程:将横切关注点(日志、权限、事务)从业务逻辑中分离出来,统一管理。
核心思想:不修改业务代码,却能增强业务功能。
AOP 的本质:横向抽取,统一织入
假设 20 个 Service 方法都需要日志、权限、事务。不用 AOP → 每个方法手写相同的重复代码。
用 AOP → 把这些"横切逻辑"提取到一个切面类,通过切入点表达式指定"哪些方法要增强",运行时由动态代理自动织入。
业务代码完全不知道增强逻辑的存在 —— 这就是"非侵入式增强"。
| 概念 | 含义 | 生产场景 | 代码体现 |
|---|---|---|---|
| Aspect 切面 | 横切关注点的模块化(日志、事务等) | 一个日志切面类,统一处理所有 Service 的日志 | @Aspect 类 |
| JoinPoint 连接点 | 程序执行的某个点(方法调用等) | 所有可以被拦截的方法执行点 | 方法的执行点 |
| Pointcut 切入点 | 定义在哪些连接点生效(表达式匹配) | 指定"只拦截 Service 层所有方法" | @Pointcut("execution(* com..*(..))") |
| Advice 通知 | 在切入点处做什么(增强逻辑) | 方法执行前记录参数、执行后记录耗时 | @Before / @After / @Around |
| Target 目标对象 | 被代理的原始对象 | 被增强的 UserService 原始 Bean | 原始 Bean |
| Proxy 代理对象 | AOP 创建的增强对象 | 运行时生成的代理类,包装了原始 Bean + 增强逻辑 | JDK / CGLIB 代理 |
| Weaving 织入 | 将切面应用到目标对象的过程 | Spring 在运行时通过动态代理完成织入 | 运行时动态代理 |
| 通知类型 | 注解 | 执行时机 | 能否获取返回值 | 能否捕获异常 |
|---|---|---|---|---|
| 前置通知 | @Before | 目标方法执行前 | ❌ | ❌ |
| 返回通知 | @AfterReturning | 方法正常返回后 | ✅ returning 属性 | ❌ |
| 异常通知 | @AfterThrowing | 方法抛出异常后 | ❌ | ✅ throwing 属性 |
| 后置通知 | @After | 方法执行后(类似 finally) | ❌ | ❌ |
| 环绕通知 | @Around | 包围目标方法(最强大) | ✅ | ✅ |
执行顺序(Spring 5.2.7+):
@Around(before部分) → @Before → 目标方法 → @AfterReturning / @AfterThrowing → @After → @Around(after部分)
| 对比项 | JDK 动态代理 | CGLIB 代理 |
|---|---|---|
| 实现原理 | 基于接口,实现 InvocationHandler | 基于继承,生成目标类的子类 |
| 核心 API | Proxy.newProxyInstance() | Enhancer + MethodInterceptor |
| 前提条件 | 目标类必须实现接口 | 目标类不能是 final 类 目标方法不能是 final |
| 生成速度 | 快 | 慢(需要生成字节码) |
| 调用性能 | 稍慢(反射调用) | 更快(FastClass 机制) |
| Spring 默认策略 | 有接口时使用 JDK | 无接口时使用 CGLIB |
| Spring Boot 默认 | — | ✅ 全部用 CGLIB(2.x 起) |
AOP 面试必答模板:
1. AOP 是什么?→ 面向切面编程,将横切关注点从业务逻辑中分离
2. 核心原理?→ 动态代理(JDK 或 CGLIB)
3. JDK vs CGLIB?→ JDK 基于接口,CGLIB 基于继承
4. 应用场景?→ 日志、权限、事务、性能监控
5. Spring Boot 默认?→ CGLIB(proxyTargetClass=true)
A 依赖 B,B 又依赖 A。创建 A 时需要 B,创建 B 时又需要 A,形成死循环。
循环依赖的本质:A 依赖 B,B 依赖 A。创建 A 时需要 B,创建 B 时又需要 A —— 死循环
Spring 的解法:把"实例化"和"初始化"拆开。A 先实例化(分配内存,半成品),把半成品引用暴露出去。B 创建时能拿到 A 的半成品引用,B 完成初始化后,A 继续拿到完整的 B,流程走通
关键前提:必须能先 new 出一个半成品 → 构造器注入在 new 时就需要依赖,所以无法解决
| 级别 | 缓存名称 | 存的内容 | 状态 |
|---|---|---|---|
| 第一级 | singletonObjects | 完整的单例 Bean(完全初始化好的) | 完全就绪 ✅ |
| 第二级 | earlySingletonObjects | 早期引用(实例化了但还没初始化完) | 半成品(有引用,属性未注入) |
| 第三级 | singletonFactories | ObjectFactory(能生成早期引用的工厂) | 工厂延迟生成(按需创建代理) |
因为构造器注入在实例化阶段就需要依赖对象,而 Spring 解决循环依赖的前提是:先实例化出一个"半成品"对象(已经 new 出来,只是没初始化完)。构造器注入连 new 都 new 不出来,自然无法解决。
为了处理 AOP 代理的情况。
如果只有两级缓存:在实例化后就要决定是否创建代理对象。但 AOP 代理本应在 BeanPostProcessor 后置处理中生成(符合生命周期规范)。
有了三级缓存(ObjectFactory):只有在真正被循环依赖引用时,才通过工厂提前生成代理对象。如果没有循环依赖,代理还是在正常的后置处理阶段生成,保证生命周期完整性。
循环依赖面试必答模板:
1. 什么是循环依赖?→ A 依赖 B,B 依赖 A
2. Spring 怎么解决?→ 三级缓存
3. 三级缓存分别存什么?→ 一级完整Bean、二级早期引用、三级对象工厂
4. 为什么要三级不是两级?→ 为了处理 AOP,保证代理在正确时机生成
5. 什么情况解决不了?→ 构造器注入的循环依赖、prototype 作用域的循环依赖
三级缓存记忆口诀:
一级存完整,二级存半成品,三级存工厂
解决不了:构造器注入(new 都 new 不出来)
关键点:三级缓存是为了 AOP 代理!
Spring 框架的源码中大量运用了经典设计模式。理解这些模式不仅有助于面试,更能帮助你在读 Spring 源码时快速理解设计意图。
作用:创建和管理对象,不暴露创建逻辑
Spring 源码体现:BeanFactory 统一创建和管理 Bean,调用方不需要知道创建细节
BeanFactory.getBean("beanName")
作用:保证一个 Bean 在容器中只有一个实例
Spring 源码体现:每个 Bean 在容器中只有一个实例,通过 singletonObjects 缓存保证
singletonObjects 缓存实现
作用:不修改原对象,增强功能
Spring 源码体现:代理对象包装原始 Bean,外部调用代理 → 增强 + 原始逻辑
JDK Proxy / CGLIB
作用:定义算法骨架,子类实现具体步骤
Spring 源码体现:JdbcTemplate 定义了获取连接→执行→关闭的骨架,你只写 RowMapper
AbstractApplicationContext.refresh()
作用:事件驱动,一对多通知
Spring 源码体现:ApplicationContext.publishEvent() 发布事件,所有 @EventListener 自动收到
publishEvent() / @EventListener
作用:统一不同 Handler 的调用方式
Spring 源码体现:不同类型的 Controller(@RequestMapping / HttpRequestHandler)通过各自 Adapter 统一调用
DispatcherServlet 调用 HandlerAdapter
作用:同一接口,不同实现策略,运行时选择
Spring 源码体现:Resource 接口有多种实现(ClassPath/FileSystem/URL),运行时按需选择
ClassPathResource / FileSystemResource / UrlResource — 都是 Resource 接口的实现
Spring 设计模式记忆口诀:
工厂造Bean,单例存一份,代理做增强,模板定流程,观察者发事件,适配器统一调用,策略选实现
自动配置 · 启动流程 · SpringMVC · 事务管理 · 注解速查
自动配置的本质:约定大于配置
你只要引入 spring-boot-starter-web,Spring Boot 自动帮你注册:
· 内嵌服务器(Embedded Tomcat)
· 前端控制器(DispatcherServlet)
· JSON 序列化器(Jackson)
· 错误处理(BasicErrorController)
这一切都是通过 @ConditionalOnXxx 条件注解实现的 —— classpath 下有相关依赖,就自动注册对应 Bean
| 注解 | 作用 | 示例场景 |
|---|---|---|
| @ConditionalOnClass | classpath 中存在指定类时生效 | 有 DataSource.class 才配置数据源 |
| @ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 | 用户没自定义 DataSource,才用默认的 |
| @ConditionalOnBean | 容器中存在指定 Bean 时生效 | 有 DataSource 才配置事务管理器 |
| @ConditionalOnProperty | 配置文件中指定属性满足条件时生效 | spring.cache.type=redis 才用 Redis 缓存 |
| @ConditionalOnWebApplication | 当前是 Web 应用时生效 | Web 环境才配置 DispatcherServlet |
| @ConditionalOnExpression | SpEL 表达式为 true 时生效 | 复杂条件组合判断 |
自动配置面试必答模板:
1. @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
2. 核心是 @EnableAutoConfiguration,通过 @Import 导入 AutoConfigurationImportSelector
3. 读取 META-INF/spring.factories 中所有 AutoConfiguration 类
4. 通过 @ConditionalOnXxx 条件过滤,满足条件的才注册 Bean
5. 所以"约定大于配置",引入 starter 就自动生效
自动配置记忆口诀:
注解导Import → 读factories → 条件过滤 → 注册Bean
核心思想:约定大于配置
| 步骤 | 方法 | 作用 | |
|---|---|---|---|
| 1 | prepareRefresh() | 刷新前准备,记录启动时间,设置状态标志 | |
| 2 | obtainFreshBeanFactory() | 创建/获取 BeanFactory,加载 BeanDefinition | |
| 3 | prepareBeanFactory() | 配置 BeanFactory 的标准特性(ClassLoader、PostProcessor等) | |
| 4 | 🔥 核心 | invokeBeanFactoryPostProcessors() | 执行 BeanFactoryPostProcessor,包括扫描 @Component 注册 BeanDefinition、处理 @Conditional 过滤自动配置 |
| 5 | registerBeanPostProcessors() | 注册 BeanPostProcessor(AOP代理、@Autowired注入等) | |
| 6 | initMessageSource() | 初始化国际化资源 | |
| 7 | initApplicationEventMulticaster() | 初始化事件广播器 | |
| 8 | 🔥 关键 | finishBeanFactoryInitialization() | 实例化所有非懒加载的单例 Bean!这一步最耗时 |
| 9 | finishRefresh() | 发布 ContextRefreshedEvent,完成刷新 |
启动流程的本质:初始化 IoC 容器 + 实例化所有 Bean
① main() 入口 → ② SpringApplication.run() 创建启动对象 → ③ 从 spring.factories 加载 Initializer/Listener → ④ 读取 application.yml 准备环境 → ⑤ 打印 Banner → ⑥ 创建 ApplicationContext → ⑦ refresh()(最核心!扫描注册+实例化所有 Bean) → ⑧ afterRefresh 后处理 → ⑨ callRunners 执行自定义 Runner → ⑩ 应用就绪 ✅
面试加分:说出 finishBeanFactoryInitialization() 是最耗时的一步,因为它要实例化所有单例 Bean。可以通过懒加载(@Lazy)优化启动速度。
| 对比 | CommandLineRunner | ApplicationRunner |
|---|---|---|
| 参数类型 | String... args | ApplicationArguments args |
| 参数解析 | 原始字符串数组,需要自己解析 | 封装好的参数对象,提供getOptionValues等方法 |
| 执行时机 | 容器刷新完成后 | 容器刷新完成后(稍早于CommandLineRunner) |
| 使用场景 | 简单的启动后任务 | 需要解析命令行参数的启动任务 |
SpringMVC 的核心:前端控制器模式(Front Controller Pattern)
所有 HTTP 请求统一入口 DispatcherServlet,由它负责:路由查找 → 适配调用 → 结果处理。
这个模式把请求处理流程标准化,开发者只需关注 Controller 中的业务逻辑,路由、参数绑定、视图渲染由框架完成。
| 注解 | 作用 | 使用位置 |
|---|---|---|
| @RequestBody | 将 HTTP 请求体的 JSON → Java 对象(反序列化) | 方法参数上 |
| @ResponseBody | 将 Java 对象 → HTTP 响应体的 JSON(序列化) | 方法上或类上 |
| @RestController | = @Controller + @ResponseBody(类上所有方法都返回JSON) | 类上 |
| 对比 | Filter 过滤器 | HandlerInterceptor 拦截器 |
|---|---|---|
| 规范 | Servlet 规范 | Spring MVC 规范 |
| 作用范围 | 所有请求(包括静态资源) | 只拦截 Controller 方法 |
| 执行时机 | DispatcherServlet 之前 | DispatcherServlet 之后、Controller 之前 |
| 能否获取 Handler 信息 | ❌ 不能 | ✅ 能获取 Controller、方法名等 |
| 能否注入 Spring Bean | 需要额外配置 | ✅ 直接 @Autowired |
| 典型用途 | 编码转换、XSS过滤、全局参数处理 | 登录校验、权限检查、日志记录 |
| 执行顺序 | 先 Filter | 后 Interceptor |
完整执行链:Filter → DispatcherServlet → Interceptor.preHandle → Controller → Interceptor.postHandle → View渲染 → Interceptor.afterCompletion → Filter
SpringMVC 流程记忆:
请求 → 前端控制器(总调度) → 处理器映射(找方法) → 适配器(调方法) → Controller → 视图/JSON → 响应
@RestController = @Controller + @ResponseBody
核心:基于 AOP 动态代理
Spring 通过 AOP 在方法执行前开启事务,方法正常返回时提交事务,抛出异常时回滚事务。
事务的本质:ACID 原子性
一个业务操作包含多条 SQL(扣库存 + 扣余额 + 创建订单),它们要么全部成功提交,要么全部回滚。绝不能出现"扣了库存但没创建订单"的中间状态。这就是事务的原子性(Atomicity)。
| 对比 | REQUIRED(默认) | REQUIRES_NEW |
|---|---|---|
| 行为 | 有事务就加入,共用同一个事务 | 总是新建独立事务,挂起原来的 |
| 回滚影响 | 任一方法异常,整个事务回滚 | 内层事务独立,不影响外层 |
| 事务关系 | 共用同一个事务,同生共死 | 两个独立事务,互不影响 |
| 使用场景 | 大部分业务方法 | 日志记录(不影响主业务)、独立子流程 |
Spring AOP 默认只对 public 方法生效。protected/private/package-visible 方法加 @Transactional 不生效。
内部调用走的是 this(原始对象),不走代理对象,所以 AOP 不生效。这是最常考的失效场景!
解决方案:① 注入自身 ② AopContext.currentProxy() ③ 拆到另一个 Service
方法内 catch 了异常但没有 re-throw,Spring 以为方法正常返回,就提交了事务。
解决方案:catch 后再 throw,或手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
Spring 默认只对 RuntimeException 和 Error 回滚。抛出 IOException 等 checked 异常不会回滚。
解决方案:@Transactional(rollbackFor = Exception.class) 指定回滚所有异常
MySQL 的 MyISAM 引擎不支持事务!必须用 InnoDB 引擎。
检查:SHOW CREATE TABLE xxx; 确认 ENGINE=InnoDB
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|---|---|---|---|
| READ_UNCOMMITTED | ❌ 可能 | ❌ 可能 | ❌ 可能 | 最低级别,基本不用 |
| READ_COMMITTED | ✅ 避免 | ❌ 可能 | ❌ 可能 | Oracle 默认 |
| REPEATABLE_READ | ✅ 避免 | ✅ 避免 | ⚠️ 可能 | MySQL 默认 ⭐ |
| SERIALIZABLE | ✅ 避免 | ✅ 避免 | ✅ 避免 | 最高级别,性能最差 |
记忆方法:隔离级别从低到高,安全性越来越高,但性能越来越差。
MySQL 默认 REPEATABLE_READ,实际通过 MVCC + Next-Key Lock 基本解决了幻读问题。
事务失效总结(面试必背):
① 非public方法 ② 内部this调用 ③ 异常被catch吞 ④ checked异常默认不回滚 ③ 数据库不支持事务
事务传播机制记忆口诀:
REQUIRED(默认加入)、REQUIRES_NEW(独立新开)是核心!
事务失效五板斧:非public、内部调、吞异常、checked、MyISAM
@Component 是通用注解;@Service/@Repository/@Controller 是语义化的特化注解,功能相同
| 对比 | @Component | @Bean |
|---|---|---|
| 使用位置 | 类上 | @Configuration 类的方法上 |
| Bean 名称 | 默认类名首字母小写 | 默认方法名,可自定义 |
| 适用范围 | 自己写的类 | 第三方库的类(你无法修改源码加@Component) |
| 灵活性 | 简单,直接标记 | 更灵活,可以加逻辑判断 |
| 对比 | @ConfigurationProperties | @Value |
|---|---|---|
| 功能 | 批量绑定配置属性到 Bean | 逐个注入配置值 |
| 前缀匹配 | ✅ prefix="spring.datasource" | ❌ 每个 @Value("${xxx}") 单独写 |
| 类型安全 | ✅ 自动类型转换 | ⚠️ 需要确保类型匹配 |
| 松散绑定 | ✅ user-name → userName | ❌ 必须精确匹配 |
| SpEL | ❌ 不支持 | ✅ @Value("#{systemProperties.xxx}") |
| 适用场景 | 一组相关配置(数据源、缓存等) | 单个配置值注入 |
IoC(控制反转):对象的创建和管理权从程序代码转移到 Spring 容器。以前自己 new 对象,现在由容器统一管理。
AOP(面向切面编程):将横切关注点(日志、权限、事务)从业务代码中抽离,通过动态代理统一织入。不修改业务代码就能增强功能。
IoC 是 Spring 的基础,AOP 是 Spring 的利器。IoC 管对象,AOP 管增强。
核心流程:实例化 → 属性注入(DI) → Aware回调 → BeanPostProcessor前置 → 初始化(@PostConstruct → afterPropertiesSet → init-method) → BeanPostProcessor后置(AOP代理在此生成) → 就绪 → 销毁(@PreDestroy → destroy → destroy-method)
面试关键点:AOP 代理是在 BeanPostProcessor 的后置处理中生成的,不是初始化阶段!
通过 三级缓存 解决:
• 一级 singletonObjects:完整的 Bean
• 二级 earlySingletonObjects:早期引用(半成品)
• 三级 singletonFactories:对象工厂(需要时才生成早期引用)
核心思路:先实例化半成品对象放入缓存,让循环依赖能拿到引用,后续再完成初始化。
注意:构造器注入的循环依赖无法解决(因为连半成品都 new 不出来)。
• JDK 动态代理:基于接口,目标类必须实现接口。使用 Proxy.newProxyInstance()。生成快,调用稍慢。
• CGLIB:基于继承,生成目标类的子类。不能代理 final 类。生成慢,调用快(FastClass 机制)。
• Spring Boot 2.x 默认:全部使用 CGLIB(proxy-target-class=true)
核心流程:
1. @SpringBootApplication 包含 @EnableAutoConfiguration
2. 通过 @Import(AutoConfigurationImportSelector) 导入选择器
3. SpringFactoriesLoader 读取 META-INF/spring.factories 中所有 AutoConfiguration 类
4. 通过 @ConditionalOnXxx 条件过滤,满足条件的才注册 Bean
5. 所以引入 starter 后自动生效 — 约定大于配置
• @Autowired:Spring 提供,先 byType 再 byName,配合 @Qualifier 指定名称
• @Resource:JSR-250 标准(Java),先 byName 再 byType,通过 name 属性指定
• @Autowired 支持 required=false,@Resource 不支持
• 推荐优先使用 @Autowired(Spring 生态更好集成)
5 大失效场景:
1. 方法不是 public(AOP 只对 public 方法生效)
2. 同一个类内部调用 this.method()(不走代理,AOP 不生效)
3. 异常被 try-catch 吞了没抛出
4. 默认只回滚 RuntimeException,checked 异常(如 IOException)不回滚
5. 数据库引擎不支持事务(如 MyISAM)
最佳实践:@Transactional(rollbackFor = Exception.class) 加在 public 方法上
7 种传播机制,最常考两种:
• REQUIRED(默认):有事务就加入,没有就新建
• REQUIRES_NEW:总是新建独立事务,挂起当前的(互不影响)
其他:SUPPORTS / MANDATORY / NOT_SUPPORTED / NEVER / NESTED
HTTP请求 → DispatcherServlet(前端控制器) → HandlerMapping(根据URL找Controller方法) → HandlerAdapter(适配调用) → Controller(执行业务) → ViewResolver/@ResponseBody(视图解析/返回JSON) → HTTP响应
核心:DispatcherServlet 是总调度,所有请求都经过它。
main() → SpringApplication.run() → 创建 SpringApplication → 加载 Initializer/Listener → 准备环境(读配置) → printBanner → createApplicationContext → refreshContext(核心!) → afterRefresh → callRunners → 启动完成
refresh() 中最关键的两步:
• invokeBeanFactoryPostProcessors:扫描注册 BeanDefinition + 自动配置条件过滤
• finishBeanFactoryInitialization:实例化所有单例 Bean(最耗时)
• BeanFactory 是最基础的 IoC 容器接口,懒加载,getBean 时才创建对象
• ApplicationContext 是 BeanFactory 的超集,预加载,启动时就创建所有单例 Bean
• ApplicationContext 额外支持:国际化(MessageSource)、事件发布(ApplicationEvent)、AOP自动集成、资源访问(ResourceLoader)
日常开发中 99% 使用 ApplicationContext
• @Component:标注在类上,通过 @ComponentScan 扫描发现。适合自己写的类。
• @Bean:标注在 @Configuration 类的方法上,方法返回值就是 Bean。适合第三方库的类(你无法修改源码加 @Component)。
• @Bean 更灵活,可以在方法里写初始化逻辑。
• 工厂模式:BeanFactory / ApplicationContext 创建和管理 Bean
• 单例模式:Bean 默认 scope=singleton
• 代理模式:AOP 动态代理(JDK / CGLIB)
• 模板方法:JdbcTemplate / RestTemplate 定义算法骨架
• 观察者模式:ApplicationEvent + ApplicationListener 事件机制
• 适配器模式:HandlerAdapter 统一不同 Handler 调用方式
• 策略模式:Resource 接口的不同实现
• Filter:Servlet 规范,在 DispatcherServlet 之前执行,拦截所有请求(含静态资源)
• Interceptor:Spring MVC 规范,在 DispatcherServlet 之后、Controller 之前执行,只拦截 Controller 方法
• Interceptor 能获取 Handler 信息,能直接注入 Spring Bean;Filter 不方便
• 执行顺序:Filter → DispatcherServlet → Interceptor → Controller
4 步走:
1. 创建 XxxAutoConfiguration 配置类(加 @Configuration + @ConditionalOnXxx)
2. 创建 XxxProperties 类(加 @ConfigurationProperties)绑定配置
3. 在 META-INF/spring.factories 中注册 AutoConfiguration 类
4. 创建 spring-boot-starter-xxx 模块(只做依赖聚合,没有代码)
• @ConfigurationProperties:批量绑定一组配置到 Bean,支持前缀匹配、松散绑定、类型安全。适合数据源、缓存等一组配置。
• @Value:逐个注入配置值,支持 SpEL 表达式。适合单个简单值。
Spring 事件机制基于观察者模式:
• ApplicationEvent:定义事件
• ApplicationEventPublisher:发布事件(publishEvent())
• @EventListener / ApplicationListener:监听并处理事件
• 典型用途:业务解耦(如下单后异步发送通知、更新统计等)
• RestTemplate:同步 HTTP 客户端,Spring 提供。简单易用但已进入维护模式。
• WebClient:异步非阻塞 HTTP 客户端,基于 WebFlux。适合高并发场景。
• Feign:声明式 HTTP 客户端(Spring Cloud),通过接口+注解定义调用,集成了负载均衡。
新项目推荐 WebClient 或 Feign,RestTemplate 虽然还能用但已不推荐新功能使用。
优先级从高到低(高优先级覆盖低优先级):
1. 命令行参数(--server.port=8081)
2. JNDI 属性
3. Java 系统属性(System.getProperties())
4. 操作系统环境变量
5. application-{profile}.properties/yml(外部 config 目录)
6. application-{profile}.properties/yml(classpath)
7. application.properties/yml(外部 config 目录)
8. application.properties/yml(classpath 根目录)
记忆:外部 > 内部,profile > 默认,命令行最优先
• Spring:企业级 Java 框架,提供 IoC、AOP、事务管理等基础能力。配置较多(XML/Java Config)。
• Spring Boot:基于 Spring 的快速开发框架。约定大于配置,自动配置、内嵌服务器、Starter 依赖聚合、Actuator 监控。
一句话总结:Spring Boot 不是替代 Spring,而是让 Spring 用起来更简单。
| 对比 | Spring | Spring Boot |
|---|---|---|
| 配置方式 | 大量XML/Java Config | 自动配置 + application.yml |
| 服务器 | 需要外置 Tomcat | 内嵌 Tomcat/Jetty/Undertow |
| 依赖管理 | 手动管理版本 | Starter 自动管理版本 |
| 启动方式 | 部署 WAR 到服务器 | java -jar 一键启动 |
| # | 考点 | 关键回答要点 | 掌握度 |
|---|---|---|---|
| 1 | IoC / DI 是什么 | 控制反转 + 依赖注入,以前自己 new,现在容器管理。解耦、方便测试 | ✅ |
| 2 | @Autowired vs @Resource | Autowired 先类型再名称;Resource 先名称再类型 | ✅ |
| 3 | Bean 生命周期 | 实例化→注入→Aware→前置→初始化→后置(AOP代理)→使用→销毁 | ✅ |
| 4 | 三级缓存解决循环依赖 | 一级完整Bean、二级早期引用、三级工厂;构造器注入无法解决 | ✅ |
| 5 | AOP 原理 | 动态代理;JDK(接口) vs CGLIB(继承);Spring Boot 默认 CGLIB | ✅ |
| 6 | Spring 设计模式 | 工厂、单例、代理、模板方法、观察者、适配器、策略 | ✅ |
| # | 考点 | 关键回答要点 | 掌握度 |
|---|---|---|---|
| 7 | 自动配置原理 | @EnableAutoConfiguration → spring.factories → @Conditional 过滤 → 约定大于配置 | ✅ |
| 8 | 启动流程 | main → run → 准备环境 → createContext → refreshContext(核心) → callRunners | ✅ |
| 9 | SpringMVC 请求流程 | 请求 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → 响应 | ✅ |
| 10 | 事务传播机制 | REQUIRED(默认加入) vs REQUIRES_NEW(独立新开) | ✅ |
| 11 | 事务失效 5 大场景 | 非public、内部调用、吞异常、checked异常、MyISAM | ✅ |
| 12 | Filter vs Interceptor | Filter(Servlet规范, 前置) vs Interceptor(Spring MVC, 后置, 能注入Bean) | ✅ |
| 13 | 自定义 Starter | AutoConfiguration + Properties + spring.factories + Starter模块 | ⭐ |
| # | 考点 | 说明 |
|---|---|---|
| 14 | @Component vs @Bean | @Component 标自己写的类;@Bean 配第三方类 |
| 15 | ApplicationContext vs BeanFactory | 后者懒加载基础接口;前者预加载+国际化+事件+AOP |
| 16 | Spring 事件机制 | ApplicationEvent + @EventListener,观察者模式实现业务解耦 |
| 17 | 配置文件加载顺序 | 命令行 > 外部 > classpath, profile > 默认 |
| 18 | Spring vs Spring Boot | Boot 不是替代 Spring,是让 Spring 更好用(自动配置+内嵌服务器+Starter) |
面试前 30 分钟快速回顾重点:
① IoC/DI 思想 + @Autowired vs @Resource
② Bean 生命周期 8 步(记住 AOP 在后置处理!)
③ 三级缓存(一级完整、二级半成品、三级工厂)
④ JDK vs CGLIB(接口 vs 继承,Boot 默认 CGLIB)
⑤ 自动配置流程(注解→factories→Conditional→注册)
⑥ 事务失效五板斧(非public、内部调、吞异常、checked、MyISAM)
⑦ REQUIRED vs REQUIRES_NEW(加入 vs 独立新开)
面试回答技巧:先说结论(一句话),再展开细节,最后举一反三。
例如:被问"Bean 生命周期?" → 先说"8 个阶段" → 展开每一步 → 最后强调"AOP 在第 6 步后置处理中生成"这个关键点。
Spring三原则:① Bean默认单例,不要在Bean中存请求级状态 → ② @Transactional不能在类内部调用中生效 → ③ 循环依赖能用但不推荐