TOP 1:Spring 中的 Bean 是线程安全的吗?
核心答案:
| Bean 作用域 | 线程安全性 |
|---|---|
| singleton(默认) | ❌ 不安全,因为多个线程共享同一个实例 |
| prototype | ✅ 相对安全,每次获取都是新实例 |
解决方案:
- 避免在 Bean 中定义有状态的成员变量
- 使用
ThreadLocal存储线程局部变量 - 将 Bean 作用域改为
prototype(不推荐,开销大) - 使用不可变对象(
final字段)
一句话:默认 singleton 模式下,有状态的 Bean 不安全,无状态的是安全的。
TOP 2:Spring 如何解决循环依赖?
核心答案 :三级缓存
| 缓存 | 名称 | 存储内容 |
|---|---|---|
| 一级 | singletonObjects |
完全初始化好的单例 Bean |
| 二级 | earlySingletonObjects |
提前暴露的 Bean(实例化完成,未属性注入) |
| 三级 | singletonFactories |
Bean 工厂(用于生成代理对象) |
解决流程(以 A 依赖 B,B 依赖 A 为例):
#mermaid-svg-5ZYgOqBykXHUWi7e{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5ZYgOqBykXHUWi7e .error-icon{fill:#552222;}#mermaid-svg-5ZYgOqBykXHUWi7e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5ZYgOqBykXHUWi7e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5ZYgOqBykXHUWi7e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5ZYgOqBykXHUWi7e .marker.cross{stroke:#333333;}#mermaid-svg-5ZYgOqBykXHUWi7e svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5ZYgOqBykXHUWi7e p{margin:0;}#mermaid-svg-5ZYgOqBykXHUWi7e .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster-label text{fill:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster-label span{color:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster-label span p{background-color:transparent;}#mermaid-svg-5ZYgOqBykXHUWi7e .label text,#mermaid-svg-5ZYgOqBykXHUWi7e span{fill:#333;color:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e .node rect,#mermaid-svg-5ZYgOqBykXHUWi7e .node circle,#mermaid-svg-5ZYgOqBykXHUWi7e .node ellipse,#mermaid-svg-5ZYgOqBykXHUWi7e .node polygon,#mermaid-svg-5ZYgOqBykXHUWi7e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5ZYgOqBykXHUWi7e .rough-node .label text,#mermaid-svg-5ZYgOqBykXHUWi7e .node .label text,#mermaid-svg-5ZYgOqBykXHUWi7e .image-shape .label,#mermaid-svg-5ZYgOqBykXHUWi7e .icon-shape .label{text-anchor:middle;}#mermaid-svg-5ZYgOqBykXHUWi7e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5ZYgOqBykXHUWi7e .rough-node .label,#mermaid-svg-5ZYgOqBykXHUWi7e .node .label,#mermaid-svg-5ZYgOqBykXHUWi7e .image-shape .label,#mermaid-svg-5ZYgOqBykXHUWi7e .icon-shape .label{text-align:center;}#mermaid-svg-5ZYgOqBykXHUWi7e .node.clickable{cursor:pointer;}#mermaid-svg-5ZYgOqBykXHUWi7e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5ZYgOqBykXHUWi7e .arrowheadPath{fill:#333333;}#mermaid-svg-5ZYgOqBykXHUWi7e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5ZYgOqBykXHUWi7e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5ZYgOqBykXHUWi7e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ZYgOqBykXHUWi7e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5ZYgOqBykXHUWi7e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ZYgOqBykXHUWi7e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster text{fill:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e .cluster span{color:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5ZYgOqBykXHUWi7e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5ZYgOqBykXHUWi7e rect.text{fill:none;stroke-width:0;}#mermaid-svg-5ZYgOqBykXHUWi7e .icon-shape,#mermaid-svg-5ZYgOqBykXHUWi7e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ZYgOqBykXHUWi7e .icon-shape p,#mermaid-svg-5ZYgOqBykXHUWi7e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5ZYgOqBykXHUWi7e .icon-shape .label rect,#mermaid-svg-5ZYgOqBykXHUWi7e .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ZYgOqBykXHUWi7e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5ZYgOqBykXHUWi7e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5ZYgOqBykXHUWi7e :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 实例化 A
将 A 放入三级缓存
singletonFactories
填充 A 属性
发现依赖 B
实例化 B
将 B 放入三级缓存
填充 B 属性
发现依赖 A
从三级缓存获取 A
移到二级缓存
B 初始化完成
放入一级缓存
A 从一级缓存获取 B
A 初始化完成
放入一级缓存
注意 :只能解决 singleton 作用域的循环依赖,prototype 无法解决。
Spring三级缓存是解决循环依赖问题的核心机制。所谓循环依赖,就是Bean A依赖Bean B,Bean B又依赖Bean A。
为什么需要三级缓存?
- 只用一级缓存:成品和半成品混在一起,无法区分
- 只用二级缓存 :可以解决普通循环依赖,但无法处理AOP代理问题
- 三级缓存的核心作用 :通过
ObjectFactory,在真正需要暴露早期对象时再决定是返回原始对象还是代理对象(避免提前创建代理,保持代理与原始对象统一)
局限
Spring的三级缓存只能解决单例模式下的setter注入循环依赖,无法解决:
- 构造器注入的循环依赖(直接报错)
- 多例Bean的循环依赖
TOP 3:Bean 的生命周期?
#mermaid-svg-K0sxe54PJfDfOXVU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-K0sxe54PJfDfOXVU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-K0sxe54PJfDfOXVU .error-icon{fill:#552222;}#mermaid-svg-K0sxe54PJfDfOXVU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-K0sxe54PJfDfOXVU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-K0sxe54PJfDfOXVU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-K0sxe54PJfDfOXVU .marker.cross{stroke:#333333;}#mermaid-svg-K0sxe54PJfDfOXVU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-K0sxe54PJfDfOXVU p{margin:0;}#mermaid-svg-K0sxe54PJfDfOXVU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-K0sxe54PJfDfOXVU .cluster-label text{fill:#333;}#mermaid-svg-K0sxe54PJfDfOXVU .cluster-label span{color:#333;}#mermaid-svg-K0sxe54PJfDfOXVU .cluster-label span p{background-color:transparent;}#mermaid-svg-K0sxe54PJfDfOXVU .label text,#mermaid-svg-K0sxe54PJfDfOXVU span{fill:#333;color:#333;}#mermaid-svg-K0sxe54PJfDfOXVU .node rect,#mermaid-svg-K0sxe54PJfDfOXVU .node circle,#mermaid-svg-K0sxe54PJfDfOXVU .node ellipse,#mermaid-svg-K0sxe54PJfDfOXVU .node polygon,#mermaid-svg-K0sxe54PJfDfOXVU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-K0sxe54PJfDfOXVU .rough-node .label text,#mermaid-svg-K0sxe54PJfDfOXVU .node .label text,#mermaid-svg-K0sxe54PJfDfOXVU .image-shape .label,#mermaid-svg-K0sxe54PJfDfOXVU .icon-shape .label{text-anchor:middle;}#mermaid-svg-K0sxe54PJfDfOXVU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-K0sxe54PJfDfOXVU .rough-node .label,#mermaid-svg-K0sxe54PJfDfOXVU .node .label,#mermaid-svg-K0sxe54PJfDfOXVU .image-shape .label,#mermaid-svg-K0sxe54PJfDfOXVU .icon-shape .label{text-align:center;}#mermaid-svg-K0sxe54PJfDfOXVU .node.clickable{cursor:pointer;}#mermaid-svg-K0sxe54PJfDfOXVU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-K0sxe54PJfDfOXVU .arrowheadPath{fill:#333333;}#mermaid-svg-K0sxe54PJfDfOXVU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-K0sxe54PJfDfOXVU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-K0sxe54PJfDfOXVU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-K0sxe54PJfDfOXVU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-K0sxe54PJfDfOXVU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-K0sxe54PJfDfOXVU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-K0sxe54PJfDfOXVU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-K0sxe54PJfDfOXVU .cluster text{fill:#333;}#mermaid-svg-K0sxe54PJfDfOXVU .cluster span{color:#333;}#mermaid-svg-K0sxe54PJfDfOXVU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-K0sxe54PJfDfOXVU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-K0sxe54PJfDfOXVU rect.text{fill:none;stroke-width:0;}#mermaid-svg-K0sxe54PJfDfOXVU .icon-shape,#mermaid-svg-K0sxe54PJfDfOXVU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-K0sxe54PJfDfOXVU .icon-shape p,#mermaid-svg-K0sxe54PJfDfOXVU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-K0sxe54PJfDfOXVU .icon-shape .label rect,#mermaid-svg-K0sxe54PJfDfOXVU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-K0sxe54PJfDfOXVU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-K0sxe54PJfDfOXVU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-K0sxe54PJfDfOXVU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 销毁阶段
执行PreDestroy标注的方法
实现DisposableBean接口的destroy方法
执行XML或Bean中指定的destroy-method
初始化阶段
执行PostConstruct标注的方法
实现InitializingBean接口的afterPropertiesSet方法
执行XML或Bean中指定的init-method
实例化 - 通过构造器创建Bean实例
属性填充 - 注入Autowired Value等依赖
Bean初始化完成 可投入使用
面试口诀:
Spring Bean 的生命周期顺序为:实例化 → 属性填充 → 初始化(@PostConstruct → afterPropertiesSet → init-method)→ 使用 → 销毁(@PreDestroy → destroy → destroy-method)。
TOP 4:@Autowired 和 @Resource 的区别?
| 对比项 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring 原生 | Java 标准(JSR-250) |
| 注入方式 | byType 优先 | byName 优先 |
| 参数 | required |
name、type |
| 适用框架 | 仅 Spring | 任何 Java EE 容器 |
使用示例:
java
// @Autowired:优先按类型匹配
@Autowired
private UserService userService;
// @Resource:优先按名称匹配(name=userService)
@Resource(name = "userService")
private UserService userService;
一句话 :
@Autowired默认按类型填充,@Resource模型按名称填充。
TOP 5:Spring AOP 的原理是什么?
核心答案 :动态代理
| 场景 | 代理方式 | 条件 |
|---|---|---|
| 有接口 | JDK 动态代理 | 目标类实现了接口 |
| 无接口 | CGLIB 动态代理 | 目标类没有接口(生成子类) |
AOP 核心概念:
| 概念 | 说明 |
|---|---|
| JoinPoint | 连接点,可被增强的方法 |
| Pointcut | 切入点,实际要增强的方法集合 |
| Advice | 通知,增强的逻辑(@Before、@After、@Around) |
| Aspect | 切面 = Pointcut + Advice |
| Weaving | 织入,将切面应用到目标对象的过程 |
一句话:AOP 通过动态代理,在不修改源码的情况下给方法增强功能。
Spring 生成代理对象的时机?
| 是否有循环依赖 | 生成时机 |
|---|---|
| 有 | 实例化后生成代理 |
| 无 | 初始化后生成代理 |
Spring 哪些场景会生成代理对象?
| 场景 | 生成时机 | 生成时机 |
|---|---|---|
| 普通 AOP | @Aspect + @Around | 前置增强、后置增强 |
| 事务管理 | @Transactional | 事务提交、回滚 |
| 异步执行 | @Async | 把方法包装成任务提交给线程池 |
| 缓存管理 | @Cacheable @EnableCaching | 从缓存读取数据、数据放入缓存 |
Spring 创建代理失效的场景
| 排名 | 失效场景 | 出现率 | 代码示例(❌ 失效) | 代码示例(✅ 正确) | 核心原因 |
|---|---|---|---|---|---|
| 1 | 内部调用 | 95% | this.methodA() |
self.methodA()(注入自身代理) |
this 指向原始对象,不经过代理 |
| 2 | 非 public 方法 | 80% | @Transactional protected void methodA() |
@Transactional public void methodA() |
Spring 默认只代理 public 方法 |
| 3 | 异常被 catch 吞掉 | 70% | try { ... } catch(Exception e) { log... } |
catch(Exception e) { throw e; } 或 手动回滚 |
代理感知不到异常,无法回滚 |
| 4 | rollbackFor 配置错误 | 55% | @Transactional(抛 checked 异常) |
@Transactional(rollbackFor = Exception.class) |
默认只回滚 RuntimeException |
| 5 | final 方法 | 45% | @Transactional public final void methodA() |
移除 final 关键字 |
CGLIB 通过子类重写,final 方法无法重写 |
| 6 | final 类 | 30% | @Service public final class UserService |
移除 final 关键字 |
CGLIB 需要继承目标类,final 类无法继承 |
TOP 6:BeanFactory 和 FactoryBean的区别?
- BeanFactory 是 Spring IOC 容器的根接口,定义了 getBean() 这些基础方法,它是容器本身,负责管理所有 Bean 的创建和生命周期,默认是懒加载的。
- FactoryBean 是一个接口,开发者实现它来自定义某个复杂 Bean 的创建逻辑。FactoryBean 本身也是一个 Bean,会被 Spring 管理。创建出来的对象通过 getBean("name") 获取,如果想获取 FactoryBean 本身,需要用 & 前缀。
- 常见的使用场景比如 MyBatis 的 SqlSessionFactoryBean 就是通过 FactoryBean 来生产 SqlSessionFactory 的。"
TOP 7:BeanPostProcessor 和BeanFactoryPostProcessor的区别
一句话:BeanPostProcessor 针对 Bean 实例,在 Bean 初始化前后进行增强;BeanFactoryPostProcessor 针对 BeanDefinition,在 Bean 实例化之前修改 Bean 的定义信息。
BeanDefinition有哪些信息
"BeanDefinition 是 Bean 的元数据,包含类名、作用域、构造参数、属性值、生命周期方法、懒加载、dependsOn 等信息,是 Spring 实例化 Bean 的依据。"
TOP 8:Spring 中 Bean 的作用域有哪些?
| 作用域 | 说明 | 使用场景 |
|---|---|---|
| singleton | 单例(默认),容器唯一 | 无状态 Bean |
| prototype | 原型,每次获取都新建 | 有状态 Bean |
| request | 每个 HTTP 请求一个 | Web 应用 |
| session | 每个 HTTP Session 一个 | Web 应用 |
| application | 每个 ServletContext 一个 | Web 应用 |
java
@Scope("prototype")
@Component
public class PrototypeBean { }
TOP 9:@SpringBootApplication 包含哪些注解?
核心答案 :它是三个注解的组合:
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration |
标注为配置类(底层是 @Configuration) |
@EnableAutoConfiguration |
开启自动配置 |
@ComponentScan |
组件扫描(默认扫描当前包及子包) |
等效代码:
java
@SpringBootApplication
// 等价于 ↓
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.example")
TOP 10:Spring 中常用的设计模式有哪些?
| 设计模式 | Spring 中的应用 |
|---|---|
| 工厂模式 | BeanFactory、ApplicationContext |
| 单例模式 | 默认 Bean 作用域 |
| 代理模式 | AOP 动态代理 |
| 模板模式 | JdbcTemplate、RedisTemplate |
| 观察者模式 | ApplicationListener、事件机制 |
| 适配器模式 | HandlerAdapter(Spring MVC) |
| 装饰器模式 | BeanWrapper |
| 策略模式 | InstantiationStrategy |
Spring Boot 自动装配原理
一、一句话总结
Spring Boot 自动装配通过
@SpringBootApplication注解中的@EnableAutoConfiguration,利用SpringFactoriesLoader从META-INF/spring.factories文件中加载配置类,再结合条件注解 (如@ConditionalOnClass)按需生效。
高频追问链
Q: Spring 如何解决循环依赖?
→ (答完后)Q: 为什么需要三级缓存?两级够吗?
→ 两级无法处理代理对象
→ Q: 什么情况下会生成代理对象?
→ AOP 会生成代理
→ Q: AOP 的原理是什么?
→ 动态代理(JDK/CGLIB)
→ Q: JDK 和 CGLIB 有什么区别?
→ JDK 需要接口,CGLIB 不需要