【面试核心】Spring Boot 高频考点全解析(整合版)
【面试核心】Spring Boot 高频考点全解析(整合版)
Spring Boot 作为 Java 后端开发的主流框架,其核心特性(IOC、AOP、自动装配等)及底层原理是面试重中之重。本文按「基础→进阶→实战」的逻辑,整合 Spring Boot 高频面试考点,从核心概念、底层原理到常见问题,层层拆解,帮助你系统梳理、从容应对面试。
一、Spring 核心基础(IOC + AOP)
Spring 是 Spring Boot 的基础,IOC 和 AOP 作为 Spring 核心思想,是所有后续知识点的前提,面试必问。
1. IOC 容器(控制反转)
核心概念
控制反转(IOC):将对象的创建、依赖注入控制权从业务代码转移到 Spring 容器,彻底解耦对象间的依赖关系,降低代码耦合度。
IOC 容器核心:以ApplicationContext为核心接口(高级容器),继承BeanFactory(基础容器),主要实现类有AnnotationConfigApplicationContext(注解配置)、XmlWebApplicationContext(XML 配置)。
BeanDefinition:描述 Bean 的元数据(类名、作用域、依赖关系、初始化/销毁方法等),IOC 容器通过 BeanDefinition 管理所有 Bean。
实现原理
Bean 定义加载:Spring 扫描
@Component、@Service、@Controller等注解,将类解析为 BeanDefinition,注册到容器中;Bean 实例化:通过反射机制创建 Bean 实例;
依赖注入:容器自动将依赖的 Bean 注入到当前 Bean 中(如
@Autowired注入);Bean 初始化:执行初始化方法,完成 Bean 就绪;
Bean 销毁:容器关闭时,执行销毁方法,释放资源。
面试高频问题
- Q:BeanFactory 和 ApplicationContext 的区别?
A:① 加载时机:BeanFactory 懒加载(获取 Bean 时才实例化),ApplicationContext 启动时实例化所有单例 Bean;② 功能范围:ApplicationContext 继承 BeanFactory,额外支持事件发布、资源加载、国际化等高级功能。
2. AOP(面向切面编程)
核心概念
AOP(面向切面编程):通过“横切”的方式,将日志、事务、权限等通用横切逻辑与业务逻辑分离,降低代码冗余,提高可维护性。核心概念包括:
切面(Aspect):横切逻辑的封装(如日志切面、事务切面),需用
@Aspect注解标记;切点(Pointcut):匹配需要增强的方法(如
execution(* com.example.service.*.*(..))匹配 service 包下所有方法);通知(Advice):增强逻辑的执行时机,分为 5 种:
前置通知(Before):目标方法执行前执行;
后置通知(After):目标方法执行后执行(无论是否抛出异常);
返回通知(AfterReturning):目标方法正常返回后执行;
异常通知(AfterThrowing):目标方法抛出异常后执行;
环绕通知(Around):包裹目标方法执行,可控制方法是否执行、修改入参和返回值(灵活度最高)。
实现原理
Spring AOP 底层基于动态代理,根据目标类是否实现接口,选择不同的代理方式:
JDK 动态代理:目标类实现接口时使用,代理类实现目标接口,通过反射调用目标方法;
CGLIB 动态代理:目标类无接口时使用,代理类继承目标类,通过修改字节码实现增强。
核心流程:定义切面和切点 → Spring 扫描切面 → 为目标类创建代理对象 → 调用目标方法时触发通知 → 执行增强逻辑 + 目标方法。
代码示例(简单切面)
@Aspect@ComponentpublicclassLogAspect{// 切点:匹配service包下所有方法@Pointcut("execution(* com.example.service.*.*(..))")publicvoidservicePointcut(){}// 前置通知@Before("servicePointcut()")publicvoidbefore(JoinPointjoinPoint){System.out.println("方法执行前:"+joinPoint.getSignature().getName());}// 环绕通知@Around("servicePointcut()")publicObjectaround(ProceedingJoinPointjoinPoint)throwsThrowable{longstart=System.currentTimeMillis();Objectresult=joinPoint.proceed();// 执行目标方法longend=System.currentTimeMillis();System.out.println("方法耗时:"+(end-start)+"ms");returnresult;}}面试高频问题
Q:Spring AOP 和 AspectJ 的区别?
A:① 实现方式:Spring AOP 基于动态代理(运行时增强),AspectJ 基于字节码修改(编译期/类加载期增强);② 增强范围:Spring AOP 仅支持方法增强,AspectJ 支持字段、构造器、方法等多种增强;③ 依赖:Spring AOP 无需额外依赖,AspectJ 需要导入专门的依赖。Q:环绕通知的核心是什么?
A:必须调用ProceedingJoinPoint.proceed()方法执行目标方法,否则目标方法不会执行;同时可通过该方法控制目标方法的入参、返回值和异常处理。
二、Bean 生命周期 + 循环依赖
Bean 生命周期和循环依赖是 IOC 容器的延伸考点,面试常结合 IOC 原理一起考察,重点掌握“全流程”和“循环依赖解决方案”。
1. Bean 生命周期(完整版)
Bean 的生命周期是从“实例化”到“销毁”的全流程,核心分为 4 个阶段:实例化 → 依赖注入 → 初始化 → 销毁,中间包含多个扩展点。
实例化:通过反射创建 Bean 实例(
BeanFactory调用createBeanInstance());属性注入:将依赖的 Bean 注入到当前 Bean 中(依赖注入的核心阶段);
初始化前准备:
执行
BeanNameAware接口:设置 Bean 的名称;执行
BeanFactoryAware接口:设置 BeanFactory 引用;执行
ApplicationContextAware接口:设置 ApplicationContext 引用;
初始化:
执行
BeanPostProcessor前置处理(postProcessBeforeInitialization());执行初始化方法(顺序:
@PostConstruct注解方法 → 实现InitializingBean接口的afterPropertiesSet()方法 → 自定义 init-method 方法);执行
BeanPostProcessor后置处理(postProcessAfterInitialization());
Bean 就绪:Bean 实例完成所有初始化,可被应用程序使用;
销毁:容器关闭时执行(顺序:
@PreDestroy注解方法 → 实现DisposableBean接口的destroy()方法 → 自定义 destroy-method 方法)。
面试高频问题
Q:BeanPostProcessor 的作用?
A:Bean 初始化前后的扩展点,可修改 Bean 实例(如 AOP 动态代理就是在后置处理中为 Bean 创建代理对象)、添加额外属性等。Q:
@PostConstruct和afterPropertiesSet的执行顺序?
A:@PostConstruct先执行,再执行afterPropertiesSet(),最后执行自定义的 init-method 方法。
2. Spring 循环依赖
核心概念
循环依赖:两个或多个 Bean 互相依赖(如 A 依赖 B,B 依赖 A;或 A→B→C→A),Spring 仅能解决部分场景的循环依赖。
循环依赖的 3 种场景
构造器循环依赖:A 的构造器依赖 B,B 的构造器依赖 A →无法解决,会抛出
BeanCurrentlyInCreationException;字段/setter 循环依赖(单例 Bean):A 的字段依赖 B,B 的字段依赖 A →可解决,核心依赖三级缓存;
字段/setter 循环依赖(原型 Bean):A 和 B 都是原型 Bean →无法解决,因为原型 Bean 每次获取都会新建,不缓存。
Spring 解决单例循环依赖的核心:三级缓存
Spring 通过“三级缓存”提前暴露未完成初始化的 Bean 实例,解决单例 Bean 的字段/setter 循环依赖,三级缓存的作用如下:
一级缓存(singletonObjects):存放完全初始化完成的单例 Bean(最终可用的 Bean);
二级缓存(earlySingletonObjects):存放提前暴露的 Bean 实例(未完成初始化,仅完成实例化和属性注入);
三级缓存(singletonFactories):存放 Bean 工厂(lambda 表达式),用于创建提前暴露的 Bean 实例(支持 AOP 动态代理,避免重复创建代理对象)。
核心解决流程(A→B→A)
Spring 启动,创建 Bean A,完成实例化后,将 A 的 Bean 工厂放入三级缓存;
为 A 注入依赖 Bean B,发现 B 未创建,开始创建 Bean B;
创建 Bean B,完成实例化后,将 B 的 Bean 工厂放入三级缓存;
为 B 注入依赖 Bean A,从三级缓存中获取 A 的 Bean 工厂,创建 A 的提前实例,放入二级缓存,注入到 B 中;
Bean B 完成初始化,放入一级缓存,删除 B 在二、三级缓存中的数据;
Bean A 注入 B 后,完成初始化,放入一级缓存,删除 A 在二、三级缓存中的数据;
循环依赖解决,A 和 B 均可用。
面试高频问题
Q:为什么原型 Bean 无法解决循环依赖?
A:原型 Bean 的核心特性是“每次获取都新建实例”,Spring 不缓存原型 Bean,无法提前暴露未初始化的实例,因此无法解决循环依赖。Q:三级缓存为什么不能简化为二级缓存?
A:为了支持 AOP 动态代理。如果只有二级缓存,提前暴露的是 Bean 原始实例,后续如果需要为 Bean 创建代理对象,会导致代理对象和原始实例不一致;三级缓存存放 Bean 工厂,在需要时才创建代理对象,保证 Bean 实例的一致性。
三、Spring 事务及传播行为
Spring 事务是保证数据一致性的核心,面试重点考察“事务原理”“传播行为”“隔离级别”及“注解失效场景”。
1. 事务核心原理
Spring 事务底层基于 AOP 实现,通过TransactionInterceptor拦截目标方法,完成事务的开启、提交、回滚操作。
Spring 事务的两种实现方式:
声明式事务(推荐):通过
@Transactional注解实现,无需手动控制事务,Spring 自动管理;编程式事务:通过
TransactionTemplate手动控制事务(开启、提交、回滚),灵活度高,适用于复杂事务场景。
2. 事务传播行为(核心考点)
事务传播行为定义:当一个事务方法调用另一个事务方法时,事务的传播规则(即新事务是否创建、当前事务是否挂起等)。常用传播行为有 7 种,重点掌握前 4 种:
| 传播行为 | 核心含义 | 适用场景 |
|---|---|---|
| REQUIRED(默认) | 若当前有事务,则加入当前事务;若无事务,则新建一个事务。 | 大多数业务场景(如订单创建、用户注册) |
| REQUIRES_NEW | 无论当前是否有事务,都新建一个事务,挂起当前事务(若存在)。 | 日志记录、操作审计(独立事务,不影响主事务) |
| SUPPORTS | 若当前有事务,则加入;若无,则以非事务方式执行。 | 查询操作(可选事务,不强制) |
| NOT_SUPPORTED | 以非事务方式执行,挂起当前事务(若存在)。 | 无需事务的操作(如缓存更新) |
| MANDATORY | 必须在事务中执行,若无事务则抛出异常。 | 强制要求事务的核心操作 |
| NEVER | 必须在非事务中执行,若有事务则抛出异常。 | 禁止事务的操作 |
| NESTED | 嵌套事务,当前事务回滚不影响外层事务,外层事务回滚则嵌套事务也回滚。 | 复杂业务中的子事务(如订单创建时的库存扣减) |
代码示例
@ServicepublicclassOrderService{@AutowiredprivateUserServiceuserService;// REQUIRED:默认传播行为,加入当前事务@Transactional(propagation=Propagation.REQUIRED)publicvoidcreateOrder(){// 订单业务逻辑userService.updateUserBalance();// 加入当前事务,一起提交/回滚}// REQUIRES_NEW:新建独立事务,不影响主事务@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidlogOrder(){// 日志记录逻辑,即使主事务回滚,日志也会提交}}3. 事务隔离级别
事务隔离级别用于解决并发事务中的问题(脏读、不可重复读、幻读),Spring 支持 4 种隔离级别,默认使用数据库的隔离级别(如 MySQL 默认为 REPEATABLE_READ):
READ_UNCOMMITTED(读未提交):最低级别,允许读取未提交的数据,可能出现脏读;
READ_COMMITTED(读已提交):允许读取已提交的数据,避免脏读,可能出现不可重复读;
REPEATABLE_READ(可重复读):保证多次读取同一数据结果一致,避免脏读、不可重复读,可能出现幻读;
SERIALIZABLE(串行化):最高级别,事务串行执行,避免所有并发问题,性能最低。
面试高频问题
Q:
@Transactional注解失效的场景?
A:① 方法非 public(Spring 事务仅支持 public 方法);② 异常被 try-catch 捕获未抛出(Spring 事务默认只有抛出 RuntimeException 才回滚);③ 自调用(类内部方法调用,AOP 无法拦截);④ 未配置事务管理器(如未引入数据源依赖,导致事务管理器无法创建);⑤ 注解标注在接口上,且代理方式为 JDK 动态代理(接口方法的注解不会被实现类继承)。Q:如何让
@Transactional注解对特定异常回滚?
A:通过@Transactional(rollbackFor = Exception.class)指定需要回滚的异常类型,默认只对 RuntimeException 回滚。
四、依赖注入:@Autowired、@Resource、@Inject 区别
三者均用于实现依赖注入,面试常考察其“来源、注入依据、使用场景”的区别,是基础高频考点。
核心对比表
| 特性 | @Autowired | @Resource | @Inject |
|---|---|---|---|
| 来源 | Spring 自带注解 | JDK 注解(javax.annotation) | JSR-330 规范注解(javax.inject) |
| 注入依据 | 优先按类型注入,可配合@Qualifier按名称注入 | 优先按名称注入,无名称时回滚到按类型注入 | 同 @Autowired(优先按类型),需配合@Named按名称注入 |
| 核心属性 | required(默认 true,必须注入,否则报错) | name(指定 Bean 名称)、type(指定 Bean 类型) | 无 required 属性,需配合@Nullable允许空值 |
| 适用场景 | Spring 项目首选,灵活度高 | 跨框架项目(兼容非 Spring 环境) | 追求标准化的项目(遵循 JSR-330 规范) |
代码示例
// 1. @Autowired + @Qualifier 按名称注入(解决多类型匹配问题)@Autowired@Qualifier("userService")privateUserServiceuserService;// 2. @Resource 按名称注入@Resource(name="userService")privateUserServiceuserService;// 3. @Inject 注入(需导入 javax.inject 依赖)@Inject@Named("userService")// @Named 替代 @QualifierprivateUserServiceuserService;面试高频问题
Q:@Autowired 注入时,存在多个相同类型的 Bean 会怎样?
A:会抛出NoUniqueBeanDefinitionException,解决方案:① 用@Qualifier指定 Bean 名称;② 用@Primary标记默认 Bean。Q:@Resource 和 @Autowired 的核心区别?
A:核心是注入依据不同:@Autowired 优先按类型,@Resource 优先按名称;@Autowired 是 Spring 注解,@Resource 是 JDK 注解,跨框架兼容性更好。
五、Spring Boot 核心特性(启动流程 + 自动装配 + 配置加载)
Spring Boot 的核心优势是“约定优于配置”,核心特性包括启动流程、自动装配、配置加载,是面试的重点压轴内容。
1. Spring Boot 启动流程(完整版)
Spring Boot 启动的核心入口是SpringApplication.run(),整个流程分为“初始化 → 环境准备 → 上下文刷新 → 容器启动 → 启动完成”5 个阶段,简化流程如下:
执行
SpringApplication.run(Application.class, args),初始化SpringApplication实例:设置应用类型(判断是否为 Web 应用);
加载启动监听器(
SpringApplicationRunListener)和初始化器(ApplicationContextInitializer);
环境准备:加载系统属性、环境变量、配置文件(application.properties/yml)等,封装为
Environment对象;创建并刷新应用上下文(
ApplicationContext):调用
AbstractApplicationContext.refresh()(核心方法);扫描组件(@Component、@Service 等),加载 BeanDefinition;
执行自动装配,创建并初始化所有 Bean;
启动嵌入式 Web 容器(若为 Web 项目):创建 Tomcat/Jetty 容器,注册
DispatcherServlet;执行启动扩展逻辑:调用
ApplicationRunner和CommandLineRunner(项目启动后执行自定义逻辑,如初始化数据);应用启动完成,等待接收请求。
核心扩展点(面试常问)
ApplicationRunner/CommandLineRunner:项目启动后执行自定义逻辑,区别是CommandLineRunner接收字符串数组参数,ApplicationRunner接收ApplicationArguments参数(支持解析命令行参数);ApplicationContextInitializer:应用上下文初始化前执行,用于修改上下文配置;SpringApplicationRunListener:监听启动全流程(如启动前、环境准备后、上下文刷新后)。
2. Spring Boot 自动装配底层原理
自动装配是 Spring Boot“约定优于配置”的核心,无需手动配置,Spring Boot 自动加载所需的 Bean,核心围绕@EnableAutoConfiguration注解展开。
核心注解关系
@SpringBootApplication是 Spring Boot 启动类的核心注解,是组合注解,包含 3 个核心注解:
@EnableAutoConfiguration:开启自动装配(核心);@ComponentScan:扫描当前包及子包的组件(@Component、@Service 等);@Configuration:标记当前类为配置类。
自动装配核心流程
启动应用时,
@EnableAutoConfiguration注解生效,触发AutoConfigurationImportSelector类的selectImports()方法;AutoConfigurationImportSelector扫描类路径下的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件;该文件中存放了所有自动配置类的全类名(如
DataSourceAutoConfiguration、WebMvcAutoConfiguration);Spring 加载这些自动配置类,通过
@Conditional系列注解(如@ConditionalOnClass、@ConditionalOnMissingBean)实现“按需加载”;自动配置类创建所需的 Bean,注入 IOC 容器;若用户自定义了同名 Bean,会覆盖自动配置的 Bean(用户配置优先)。
面试高频问题
Q:如何关闭某个自动配置类?
A:通过@SpringBootApplication(exclude = 自动配置类.class)排除,如@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)关闭数据源自动配置。Q:自动配置的核心逻辑是什么?
A:“扫描自动配置类 → 按需筛选(@Conditional 注解) → 注入 Bean → 用户配置覆盖自动配置”,核心是AutoConfigurationImportSelector和@Conditional注解。
3. Spring Boot 配置加载优先级
Spring Boot 支持多种配置方式,配置加载有明确的优先级,核心原则:越靠近应用运行环境的配置,优先级越高(后加载的配置会覆盖先加载的配置)。
核心优先级(从高到低,重点掌握前 10 种)
命令行参数:如
java -jar app.jar --server.port=8081,通过--传入的参数优先级最高;SPRING_APPLICATION_JSON 环境变量/系统属性:如
export SPRING_APPLICATION_JSON='{"server.port":8082}';ServletConfig 初始化参数;
ServletContext 初始化参数;
JNDI 配置(java:comp/env);
系统属性(System.getProperties()):如
System.setProperty("server.port", "8083");操作系统环境变量:如 Linux 中的
export SERVER_PORT=8084;profile 专用配置文件:
application-{profile}.properties/yml(如application-dev.yml、application-prod.yml);默认配置文件:
application.properties/yml(优先级:项目根目录 > classpath 根目录 > classpath:/config/);@PropertySource注解加载的配置;默认属性(SpringApplication.setDefaultProperties())。
面试高频问题
Q:.yml 和 .properties 配置文件的优先级?
A:同目录下,.yml优先级高于.properties,但遵循“后加载覆盖先加载”原则;若不同目录,按目录优先级(如根目录配置高于 classpath 配置)。Q:为什么命令行参数优先级最高?
A:为了方便运维人员部署时灵活修改配置(如端口、数据源地址),无需修改代码或配置文件,符合“运维便捷性”设计。
六、Spring Boot Web 相关(启动 Web 项目 + Spring MVC)
Spring Boot 整合 Spring MVC 后,无需手动配置 Tomcat 和 Spring MVC 核心组件,面试常考察“Web 项目启动原理”和“Spring MVC 请求流程”。
1. Spring Boot 如何启动 Web 项目
Spring Boot 启动 Web 项目的核心是“嵌入式容器自动装配”,无需手动部署到外部 Tomcat,流程如下:
依赖触发:引入
spring-boot-starter-web依赖,该依赖会自动引入 Tomcat(默认嵌入式容器)、Spring MVC、Spring Web 等核心依赖;容器自动配置:
ServletWebServerFactoryAutoConfiguration自动配置嵌入式容器,根据依赖判断使用 Tomcat(默认)、Jetty 或 Undertow;容器启动:Spring Boot 启动时,通过
WebServerApplicationContext创建并启动嵌入式容器,绑定默认端口(8080);注册核心组件:将 Spring MVC 的
DispatcherServlet(前端控制器)注册到嵌入式容器,绑定上下文路径(默认/);请求处理:容器监听端口,接收客户端请求,转发给
DispatcherServlet,由 Spring MVC 完成请求处理并返回响应。
面试高频问题
Q:如何切换嵌入式容器(如 Tomcat → Jetty)?
A:排除 Tomcat 依赖,引入 Jetty 依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>Q:如何指定 Web 项目的端口?
A:① 配置文件:server.port=8081;② 命令行参数:--server.port=8081;③ 编程式:通过ServerProperties设置端口。
2. Spring MVC 核心流程
Spring MVC 是 Spring 用于 Web 开发的模块,核心是“前端控制器 + 组件协作”,请求处理流程是面试重点。
核心组件
DispatcherServlet:前端控制器(核心),统一接收所有客户端请求,协调其他组件完成请求处理;HandlerMapping:请求映射器,根据请求 URL 映射到对应的处理器(Controller 方法);HandlerAdapter:处理器适配器,适配并执行处理器方法(如解析请求参数、调用 Controller 方法);ViewResolver:视图解析器,将处理器返回的ModelAndView解析为具体视图(如 Thymeleaf、JSP);ModelAndView:封装模型数据(Model)和视图名称(View),用于传递数据和指定视图。
请求处理流程(核心步骤)
客户端发送 HTTP 请求,由嵌入式容器(Tomcat)接收,转发给
DispatcherServlet;DispatcherServlet调用HandlerMapping,根据请求 URL 找到对应的处理器(Controller 方法);DispatcherServlet调用HandlerAdapter,适配并执行处理器方法;处理器方法执行完成,返回
ModelAndView(模型数据 + 视图名称);DispatcherServlet调用ViewResolver,将视图名称解析为具体视图;视图渲染模型数据,生成 HTML 响应;
DispatcherServlet将响应返回给客户端,请求处理完成。
面试高频问题
Q:DispatcherServlet 的核心作用?
A:统一接收请求,协调 HandlerMapping、HandlerAdapter、ViewResolver 等组件,降低组件间的耦合,是 Spring MVC 的“中枢”。Q:Spring MVC 的请求参数绑定原理?
A:HandlerAdapter 会根据处理器方法的参数类型,自动将请求参数(如 URL 参数、表单参数)转换为对应类型的参数,注入到方法中(如@RequestParam绑定请求参数)。
七、总结
本文按「Spring 基础→Spring Boot 核心→Web 相关」的逻辑,整合了 Spring Boot 面试所有高频考点,核心总结如下:
Spring 基础:IOC 是容器核心(三级缓存解决单例循环依赖),AOP 基于动态代理实现横切逻辑,Bean 生命周期需掌握初始化/销毁扩展点;
事务核心:声明式事务(@Transactional)是主流,传播行为(REQUIRED、REQUIRES_NEW)和注解失效场景是重点;
依赖注入:@Autowired 优先按类型,@Resource 优先按名称,掌握三者区别及使用场景;
Spring Boot 核心:自动装配依赖 @EnableAutoConfiguration 和 AutoConfigurationImportSelector,启动流程核心是上下文刷新,配置加载遵循“越靠近运行越优先”;
Web 相关:嵌入式容器自动装配启动 Web 项目,Spring MVC 核心是 DispatcherServlet 协调各组件完成请求处理。
掌握这些知识点,不仅能从容应对面试,更能深入理解 Spring Boot 底层原理,为项目开发、问题排查打下坚实基础。建议结合代码实战,加深对每个考点的理解,避免死记硬背。
整合后考点逻辑更连贯,适配面试复习的循序渐进节奏,需要我帮你补充每个考点的面试高频问答总结,方便你快速背诵吗?
