三、Spring


TOP 1:Spring 中的 Bean 是线程安全的吗?

核心答案

Bean 作用域 线程安全性
singleton(默认) ❌ 不安全,因为多个线程共享同一个实例
prototype ✅ 相对安全,每次获取都是新实例

解决方案

  1. 避免在 Bean 中定义有状态的成员变量
  2. 使用 ThreadLocal 存储线程局部变量
  3. 将 Bean 作用域改为 prototype(不推荐,开销大)
  4. 使用不可变对象(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 nametype
适用框架 仅 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 ,利用 SpringFactoriesLoaderMETA-INF/spring.factories 文件中加载配置类,再结合条件注解 (如 @ConditionalOnClass)按需生效。


高频追问链

复制代码
Q: Spring 如何解决循环依赖?
→ (答完后)Q: 为什么需要三级缓存?两级够吗?
→ 两级无法处理代理对象
→ Q: 什么情况下会生成代理对象?
→ AOP 会生成代理
→ Q: AOP 的原理是什么?
→ 动态代理(JDK/CGLIB)
→ Q: JDK 和 CGLIB 有什么区别?
→ JDK 需要接口,CGLIB 不需要

相关推荐
copyer_xyf1 小时前
Python venv 虚拟环境
前端·后端·python
橘右今1 小时前
2026 Java后端高频面试宝典
java·开发语言·面试
xyzzklk2 小时前
解决Salesforce无法向外发送邮件
android·java·开发语言·网络·crm·salesforce·客户关系管理
copyer_xyf3 小时前
Python 如何同时做很多事:进程、线程、协程
前端·后端·python
biubiubiu07063 小时前
SpringBoot关于外部化配置
java·spring boot·spring
Full Stack Developme3 小时前
Spring Bean 依赖注入
python·spring·log4j
zzz_23683 小时前
【Spring】面试突击系列(二):SpringBoot 入门与自动配置原理
java·spring boot·spring
Full Stack Developme3 小时前
Spring AOP 与 AspectJ
java·后端·spring
快乐的木子李3 小时前
最新版Maven免安装配置教程
java·maven