SpringBoot2核心功能面试题库
生成时间:2026-05-21 21:49
【面试题】1. SpringBoot自动配置原理是什么?@EnableAutoConfiguration如何工作?
【标准答案】 自动配置是SpringBoot的核心,原理有三步:
第一步:@SpringBootApplication是个组合注解,包含了@SpringBootConfiguration(就是个@Configuration)、@ComponentScan(包扫描)和@EnableAutoConfiguration(开启自动配置)。
第二步:@EnableAutoConfiguration通过@Import导入AutoConfigurationImportSelector,这个选择器会读取META-INF/spring.factories(SpringBoot 2.7之前)或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(2.7之后),加载所有自动配置类的全限定名。
第三步:条件注解过滤。每个自动配置类上都有一堆@ConditionalOnXxx注解,比如@ConditionalOnClass(类路径下有某个类才生效)、@ConditionalOnMissingBean(容器里没有才创建)、@ConditionalOnProperty(配置项满足条件才生效)。只有满足条件的配置类才会真正注册Bean。
总结: 自动配置 = 读取配置类 + 条件过滤 + 按需装配。这就解释了为什么引入starter就能自动配置好一切,因为starter引入了依赖(满足@ConditionalOnClass),自动配置类就会生效。
【面试题】2. SpringBoot的配置文件加载顺序和优先级覆盖规则是怎样的?
【标准答案】 SpringBoot的配置加载有个核心原则:外层配置覆盖内层配置,后加载覆盖先加载。
加载顺序(从高到低优先级):
- 命令行参数(--server.port=8081)
- JAR包外的application.properties/yml(config子目录 > 当前目录)
- JAR包内的application.properties/yml(config子目录 > 当前目录)
- @PropertySource注解指定的配置
- 默认属性(SpringApplication.setDefaultProperties)
具体到application.yml/properties:
./config/application.yml(项目根目录的config文件夹,优先级最高)./application.yml(项目根目录)classpath:/config/application.yml(resources/config下)classpath:/application.yml(resources下,优先级最低)
Profile多环境: application-dev.yml、application-prod.yml会覆盖application.yml中的同名配置。激活方式:spring.profiles.active=dev。
记忆口诀: 外层盖内层,命令行最霸道,config目录比平级高,Profile最后叠加。
【面试题】3. Spring Bean的完整生命周期是怎样的?BeanPostProcessor在哪里介入?
【标准答案】 Bean生命周期分四个阶段,记成口诀:实例化 → 属性赋值 → 初始化 → 销毁。
详细流程:
- 实例化:调用构造方法创建对象实例
- 属性赋值:依赖注入(@Autowired、@Value)
- 初始化前:BeanPostProcessor.postProcessBeforeInitialization()
- 初始化:依次执行 @PostConstruct → InitializingBean.afterPropertiesSet() → 自定义init-method
- 初始化后:BeanPostProcessor.postProcessAfterInitialization()(AOP代理就是在这步生成的)
- 使用中:Bean就绪,可以调用
- 销毁前:@PreDestroy → DisposableBean.destroy() → 自定义destroy-method
关键点:
- BeanPostProcessor是Spring留给开发者的扩展点,在初始化前后介入。最经典应用就是AOP:Spring会在postProcessAfterInitialization里把原始对象包装成代理对象。
- Aware接口(如ApplicationContextAware)会在初始化前调用,让Bean拿到容器的引用。
面试加分: 如果问到BeanFactoryPostProcessor,它更早,在Bean实例化之前执行,可以修改BeanDefinition(比如PropertyPlaceholderConfigurer解析占位符)。
【面试题】4. @Import注解有哪三种使用方式?分别在什么场景下用?
【标准答案】 @Import有三种用法,都可以把类导入到Spring容器:
第一种:直接导入普通类
@Import(DataSourceConfig.class)直接把DataSourceConfig注册为Bean,相当于@Bean的效果。适用场景:导入第三方类,无法加@Component等注解时。
第二种:导入ImportSelector实现类
@Import(MyImportSelector.class)
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata metadata) {
return new String[]{"com.example.ConfigA", "com.example.ConfigB"};
}
}动态选择要导入的配置类全限定名。适用场景:条件装配,比如@EnableXXX注解背后就是用ImportSelector。
第三种:导入ImportBeanDefinitionRegistrar实现类
@Import(MyRegistrar.class)
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 手动注册BeanDefinition
BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class).getBeanDefinition();
registry.registerBeanDefinition("myBean", bd);
}
}最灵活,可以手动注册BeanDefinition。适用场景:动态注册Bean,比如MyBatis的@MapperScan就是用这种方式批量注册Mapper。
对比: 第一种最简单,第二种可批量+动态选择,第三种最强大可完全控制注册过程。
【面试题】5. Spring AOP的代理方式是什么?JDK动态代理和CGLIB有什么区别?
【标准答案】 Spring AOP的代理方式选择有个明确规则:目标类实现了接口,默认用JDK动态代理;目标类没实现接口,用CGLIB代理。可以通过spring.aop.proxy-target-class=true强制全部使用CGLIB。
JDK动态代理:
- 基于接口,只能代理接口方法
- 通过反射生成代理类,代理类实现了目标接口
- 性能:调用时需要反射,但生成代理快
- 限制:被代理类必须实现至少一个接口
CGLIB代理:
- 基于继承,代理类继承目标类
- 通过字节码增强生成代理类的子类
- 性能:调用快(直接调用),但生成代理慢
- 限制:无法代理final类和final方法
SpringBoot 2.x后的变化: SpringBoot 2.x默认使用CGLIB,因为spring.aop.proxy-target-class默认为true。Spring 5.x后JDK动态代理性能优化,差距已不明显。
面试陷阱: @Transactional注解生效的关键——代理对象必须通过容器获取,自己new出来的对象不走代理,事务不会生效。类内部方法调用(this.method())也不会走代理,需要用AopContext.currentProxy()获取代理对象调用自己的方法。
【面试题】6. Spring事件机制是怎么工作的?有哪些核心内置事件?
【标准答案】 Spring事件机制是基于观察者模式的解耦方案,核心三要素:事件、监听器、发布者。
工作流程:
- 定义事件:继承ApplicationEvent
- 发布事件:通过ApplicationEventPublisher.publishEvent()
- 监听事件:实现ApplicationListener接口 或 使用@EventListener注解
两种监听方式:
// 方式一:实现接口
public class MyListener implements ApplicationListener<MyEvent> {
public void onApplicationEvent(MyEvent event) { }
}
// 方式二:注解(推荐,更简洁)
@EventListener
public void handleMyEvent(MyEvent event) { }
// 异步事件:加@Async注解
@EventListener
@Async
public void handleAsync(MyEvent event) { }核心内置事件(按执行顺序):
- ContextRefreshedEvent:容器刷新完成,所有Bean初始化完毕
- ContextStartedEvent:容器启动(调用ConfigurableApplicationContext.start())
- ContextClosedEvent:容器关闭
- ApplicationReadyEvent:SpringBoot应用完全就绪(比ContextRefreshedEvent更晚,actuator端点已可用)
应用场景:
- ContextRefreshedEvent常用于应用启动后执行初始化逻辑(比如加载缓存)
- 自定义事件用于业务解耦(比如订单创建后发送通知,不同模块监听同一事件各自处理)
【面试题】7. SpringBoot如何切换嵌入式容器?Tomcat、Undertow、Jetty有什么区别?
【标准答案】 SpringBoot默认使用Tomcat,切换方式很简单:排除Tomcat依赖,引入其他容器starter。
切换到Undertow:
<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-undertow</artifactId>
</dependency>三大容器对比:
| 容器 | 特点 | 适用场景 |
|---|---|---|
| Tomcat | 成熟稳定,生态最完善,资源占用中等 | 传统企业应用,兼容性要求高 |
| Undertow | 轻量高性能,非阻塞IO,内存占用低 | 高并发、微服务、资源受限环境 |
| Jetty | 轻量级,嵌入式友好,启动快 | 长连接、WebSocket、快速启动场景 |
性能调优(以Tomcat为例):
server:
tomcat:
threads:
max: 200 # 最大工作线程
min-spare: 10 # 最小空闲线程
max-connections: 10000 # 最大连接数
accept-count: 100 # 等待队列长度
port: 8080面试建议: 选Tomcat求稳,选Undertow求性能。Undertow在高并发场景下内存占用更低,吞吐量更高。
【面试题】8. @Transactional事务注解在什么情况下会失效?如何避免?
【标准答案】 @Transactional失效的常见原因有七种,面试必问:
1. 方法不是public
- Spring AOP只能代理public方法,private/protected/包级私有都不生效
2. 同类内部调用
- this.method()不走代理,事务无效。解决:注入自己(@Autowired Self self)或用AopContext.currentProxy()
3. 异常被catch吞掉
- 异常没抛出,事务管理器感知不到,无法回滚。解决:catch后手动抛RuntimeException或调用setRollbackOnly()
4. 异常类型不对
- 默认只对RuntimeException和Error回滚,检查型异常(如IOException)不回滚。解决:@Transactional(rollbackFor = Exception.class)
5. 数据库引擎不支持事务
- MySQL的MyISAM不支持事务,要用InnoDB
6. 多线程调用
- 事务线程绑定(ThreadLocal),新线程无法共享事务上下文
7. 传播行为配置错误
- @Transactional(propagation = Propagation.NOT_SUPPORTED)会以非事务方式运行
最佳实践:
- 统一加 rollbackFor = Exception.class
- 事务方法只做数据库操作,不要夹杂远程调用(RPC超时会导致数据库连接池耗尽)
- 大事务拆小事务,避免长事务锁表
【面试题】9. Spring缓存抽象@Cacheable的原理是什么?如何集成Redis?
【标准答案】 Spring缓存抽象是基于AOP实现的,核心原理:拦截方法调用,先查缓存,命中直接返回;未命中执行方法,结果写入缓存。
@Cacheable执行流程:
- 生成缓存Key:key属性的SpEL表达式(默认是方法参数组合)
- 查缓存:用CacheManager.get(key)
- 命中:直接返回缓存值
- 未命中:执行方法,将返回值写入缓存(CacheManager.put)
集成Redis步骤:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>spring:
cache:
type: redis
redis:
host: localhost
port: 6379常用注解:
@Cacheable(value = "user", key = "#id") // 查询缓存
public User getUser(Long id) { }
@CachePut(value = "user", key = "#user.id") // 更新缓存
public User updateUser(User user) { }
@CacheEvict(value = "user", key = "#id") // 删除缓存
public void deleteUser(Long id) { }
@CacheEvict(value = "user", allEntries = true) // 清空整个缓存空间
public void clearAllUsers() { }注意事项:
- 缓存的方法所在类必须是Spring Bean(才能被AOP代理)
- 返回对象必须可序列化(Redis要求)
- 设置过期时间:@Cacheable(value = "user", key = "#id", cacheManager = "redisCacheManager")
【面试题】10. SpringBoot Actuator有哪些核心端点?如何自定义HealthIndicator?
【标准答案】 Actuator是SpringBoot的生产级监控工具,提供了多个健康检查和指标端点。
核心端点:
| 端点 | 作用 | 默认暴露 |
|---|---|---|
| /health | 应用健康状态(DB、Redis、磁盘等) | 是 |
| /info | 应用信息(版本、名称) | 是 |
| /metrics | 性能指标(内存、线程、HTTP请求) | 否 |
| /env | 环境变量和配置属性 | 否 |
| /beans | 容器中的所有Bean | 否 |
| /mappings | URL映射关系 | 否 |
| /threaddump | 线程转储 | 否 |
自定义HealthIndicator:
@Component
public class MyServiceHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 自定义健康检查逻辑
boolean isHealthy = checkMyService();
if (isHealthy) {
return Health.up()
.withDetail("service", "my-service")
.withDetail("status", "running")
.build();
} else {
return Health.down()
.withDetail("error", "Service unavailable")
.build();
}
}
private boolean checkMyService() {
// 实际检查逻辑(如调用第三方服务、检查资源)
return true;
}
}暴露配置:
management:
endpoints:
web:
exposure:
include: health,info,metrics # 暴露指定端点
# include: "*" # 暴露所有端点(生产环境不推荐)
endpoint:
health:
show-details: always # 显示详细健康信息安全建议:
- 生产环境必须加安全认证(Spring Security)
- 敏感端点(/env、/beans)不建议暴露
- 推荐用Prometheus + Grafana可视化监控:引入micrometer-registry-prometheus依赖,暴露/prometheus端点
本套面试题覆盖考点:
- 自动配置原理(题目1)
- 配置管理(题目2)
- Bean生命周期(题目3)
- 注解驱动开发(题目4)
- AOP切面编程(题目5)
- 事件机制(题目6)
- Web开发-嵌入式容器(题目7)
- 数据访问-事务管理(题目8)
- 缓存抽象(题目9)
- 安全与监控-Actuator(题目10)
题目类型分布: 原理深挖(5题)、场景实操(3题)、性能优化(1题)、面试情景问答(1题)