SpringBoot2核心功能面试题库
生成时间:2026-05-21 21:54 | 题目数:10
【面试题】1. SpringBoot自动配置的完整流程是怎样的?从启动到Bean装配完成发生了什么?
【标准答案】这个流程分四个阶段: 第一阶段:启动类加载 @SpringBootApplication是个组合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。@EnableAutoConfiguration通过@Import导入了AutoConfigurationImportSelector,这个类会读取所有jar包里的spring.factories文件或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,加载所有自动配置类的全类名。
第二阶段:条件过滤 每个自动配置类上都有大量条件注解,比如@ConditionalOnClass检查类路径是否有某个类,@ConditionalOnMissingBean检查容器里是否已有该Bean,@ConditionalOnProperty检查配置文件里的开关。只有所有条件都满足,这个配置类才会生效,才会装配里面的Bean。
第三阶段:Bean注册 满足条件的配置类会被解析成BeanDefinition,注册到BeanFactory。此时Bean还没实例化,只是元数据。然后执行BeanFactoryPostProcessor,比如ConfigurationClassPostProcessor会处理@Configuration类上的@Bean方法。
第四阶段:实例化与回调 按需实例化Bean,执行完整的生命周期回调:实例化→属性注入→Aware接口回调→BeanPostProcessor前置处理→@PostConstruct/InitializingBean→BeanPostProcessor后置处理→放入容器。这样整个自动配置流程就完成了。
【面试题】2. @ConfigurationProperties和@Value有什么区别?生产环境推荐用哪个?
【标准答案】两者有本质区别:
@ConfigurationProperties是批量映射 一次性把配置文件里某个前缀下的所有属性映射到一个POJO类,支持嵌套对象、List、Map等复杂结构。配合@ConfigurationPropertiesScan或@EnableConfigurationProperties使用。松散绑定支持kebab-case、camelCase、下划线等多种写法。
@Value是单值注入 每个属性单独注入,@Value("${app.name}"),不支持复杂对象,不支持松散绑定,不支持校验。
生产环境推荐@ConfigurationProperties 三个原因:
- 类型安全:配置类里可以用@Validated做JSR-303校验,启动时就报错,而不是运行时NPE
- 可维护性:所有配置集中在POJO,改配置只改一个地方,而不是满世界找@Value
- IDE友好:配置类有getter/setter,代码补全、重构都方便
唯一用@Value的场景:少量配置、SpEL表达式、动态刷新(配合@RefreshScope)。其他情况优先@ConfigurationProperties。
【面试题】3. Bean的完整生命周期是怎样的?哪些扩展点可以干预?
【标准答案】Bean生命周期分四大阶段,每个阶段都有扩展点:
阶段一:实例化 BeanDefinition转换成BeanWrapper。扩展点:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation可以返回代理对象,跳过Spring默认实例化。
阶段二:属性赋值 依赖注入,@Autowired/@Value生效。扩展点:InstantiationAwareBeanPostProcessor.postProcessPropertyValues可以修改属性值。
阶段三:初始化 分四个子步骤:
- Aware接口回调:BeanNameAware、BeanFactoryAware、ApplicationContextAware,注入容器相关对象
- BeanPostProcessor.postProcessBeforeInitialization:前置处理
- 初始化回调:@PostConstruct → InitializingBean.afterPropertiesSet → @Bean的initMethod
- BeanPostProcessor.postProcessAfterInitialization:AOP代理在这里生成
阶段四:销毁 容器关闭时执行:@PreDestroy → DisposableBean.destroy → @Bean的destroyMethod
生产常用扩展点:
- BeanPostProcessor:做AOP、权限校验、日志
- InitializingBean:资源初始化、连接池预热
- @PostConstruct:启动后校验、缓存预热
记住顺序:实例化→属性注入→Aware→PostProcessBefore→初始化回调→PostProcessAfter→就绪→销毁回调。
【面试题】4. @Import的三种用法分别是什么?SpringBoot自动配置用的是哪种?
【标准答案】@Import有三种导入方式:
第一种:直接导入类 @Import(MyConfig.class),把MyConfig作为配置类注册。效果等同于在@Configuration类里写@Bean方法。最简单直接。
第二种:导入ImportSelector实现类 @Import(MyImportSelector.class),ImportSelector的selectImports方法返回要导入的类名数组,Spring会批量注册。可以在方法里做条件判断、动态计算。SpringBoot的@EnableAutoConfiguration就是用的这种,AutoConfigurationImportSelector读取spring.factories返回所有自动配置类。
第三种:导入ImportBeanDefinitionRegistrar实现类 @Import(MyRegistrar.class),可以手动注册BeanDefinition,最灵活。MyBatis的@MapperScan用的就是这种,扫描指定包下的接口,动态注册成MapperFactoryBean。
SpringBoot自动配置用的是第二种 EnableAutoConfiguration导入AutoConfigurationImportSelector,它读取spring.factories和imports文件,返回所有自动配置类名数组,Spring批量注册。这样做的好处是动态、可扩展,第三方Starter只要在spring.factories里配置自己的类就能自动生效。
【面试题】5. AOP通知的执行顺序是怎样的?多个切面如何排序?
【标准答案】单个切面的通知顺序: Around(前置部分)→ Before → 目标方法执行 → Around(后置部分)→ After → AfterReturning/AfterThrowing(二选一)
- 正常执行:Around前→Before→方法→Around后→After→AfterReturning
- 抛异常:Around前→Before→方法异常→After→AfterThrowing
多个切面的执行顺序: 多个切面形成责任链,按@Order或Ordered接口排序:
- Order值小的切面:Aspect1→Aspect2→Aspect3(进入顺序)
- 执行时是洋葱模型:1的Before→2的Before→3的Before→目标方法→3的After→2的After→1的After
- 先进后出,后进先出
实际排序方法:
- @Order(1)、@Order(2),数值越小优先级越高
- 实现Ordered接口,getOrder返回数值
- 不指定时,按切面类名字母顺序(不可靠,生产必须显式指定)
生产实践:日志切面@Order(1)最外层,事务切面@Order(2)在内层,避免事务回滚导致日志丢失。记住:数值小先进入、后退出,包在最外面。
【面试题】6. SpringBoot事件机制的完整流程是怎样的?如何实现异步监听?
【标准答案】事件机制三要素:
- 事件:继承ApplicationEvent,封装事件数据
- 发布者:ApplicationEventPublisher,容器内置
- 监听器:实现ApplicationListener或@EventListener注解
完整流程:
- 容器启动时,自动注册所有ApplicationListener Bean,并扫描@EventListener方法
- 调用publishEvent(new MyEvent(data)),事件进入多播器ApplicationEventMulticaster
- 多播器遍历所有匹配的监听器,按顺序调用onApplicationEvent或@EventListener方法
- 默认同步执行,监听器在发布线程执行,抛异常会中断流程
实现异步监听: 两种方式:
- @EventListener + @Async:在监听方法上再加@Async,异步执行,不影响主流程
- 配置线程池:@Async默认用SimpleAsyncTaskExecutor,生产要自定义ThreadPoolTaskExecutor,配置核心线程数、队列容量、拒绝策略
内置核心事件:
- ContextRefreshedEvent:容器刷新完成,常用于启动后初始化
- ContextStartedEvent:调用start()时触发
- ContextClosedEvent:容器关闭时触发,做资源清理
注意:异步监听器抛异常不会影响发布者,事务监听器要用@TransactionalEventListener,在事务特定阶段触发。
【面试题】7. SpringBoot嵌入式容器是如何启动的?如何切换Tomcat为Undertow?
【标准答案】嵌入式容器启动原理: SpringBoot通过ServletWebServerFactory创建容器,核心流程:
- SpringApplication.run()触发refreshContext()
- AbstractApplicationContext.onRefresh()调用createWebServer()
- ServletWebServerApplicationContext获取WebServerFactory(默认TomcatServletWebServerFactory)
- 工厂调用getWebServer(),创建Tomcat实例,配置端口、连接器、Context
- Tomcat.start()启动,阻塞主线程,监听端口
切换Tomcat为Undertow: 两步:
- 排除Tomcat依赖:
<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>- 引入Undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>为什么要切换: Undertow非阻塞IO,高并发场景吞吐量比Tomcat高30%+,内存占用更低。但Tomcat生态更成熟,Undertow对Servlet规范支持不完整。选型看场景:传统MVC用Tomcat,网关、高并发API用Undertow。
【面试题】8. @Transactional的传播行为有哪些?什么场景用REQUIRES_NEW?
【标准答案】七种传播行为:
- REQUIRED(默认):有事务就加入,没有就新建
- SUPPORTS:有事务就加入,没有就以非事务运行
- MANDATORY:必须有事务,否则抛异常
- REQUIRES_NEW:必须新建事务,已有事务挂起
- NOT_SUPPORTED:以非事务运行,有事务就挂起
- NEVER:以非事务运行,有事务抛异常
- NESTED:有事务就创建嵌套事务(savepoint),没有就新建
REQUIRES_NEW使用场景:
- 日志记录:主事务回滚,日志也要保存。日志方法用REQUIRES_NEW,独立事务,不受主事务影响
- 异步通知:发送消息、邮件,主事务失败也要发送
- 审计记录:操作失败也要记录审计日志
注意事项:
- REQUIRES_NEW会挂起外层事务,内层提交后再恢复外层,有性能开销
- 内层事务抛异常,外层要catch,否则外层也回滚
- 实际是两个独立事务,外层回滚不影响内层已提交的
生产慎用:频繁创建事务有性能损耗,只在必须独立提交的场景用。日志表建议用独立的DataSource或不依赖事务。
【面试题】9. SpringBoot缓存抽象@Cacheable的原理是什么?如何集成Redis?
【标准答案】@Cacheable原理: Spring缓存抽象基于AOP:
- 启动类加@EnableCaching,导入CacheAspect切面
- 调用@Cacheable方法时,切面拦截
- 根据key(默认是方法参数组合)去CacheManager查找缓存
- 命中直接返回,不执行方法;未命中执行方法,结果写入缓存
- @CachePut每次都执行方法并更新缓存,@CacheEvict删除缓存
集成Redis步骤:
- 引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>- 配置连接:
spring:
redis:
host: localhost
port: 6379- 配置序列化:
@Configuration
public class CacheConfig {
@Bean
public RedisCacheConfiguration cacheConfig() {
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofHours(1));
}
}- 使用:
@Cacheable(value = "user", key = "#id")
public User getById(Long id) { ... }注意:默认用JDK序列化,生产要改JSON序列化,否则Redis里存的是二进制,不好查看。缓存key要有命名空间,避免冲突。
【面试题】10. SpringBoot Actuator有哪些核心端点?如何保障端点安全?
【标准答案】核心端点:
- /health:健康检查,显示应用状态和组件状态(DB、Redis、Disk)
- /info:应用信息,版本、描述等
- /metrics:指标数据,JVM内存、线程数、HTTP请求数、Tomcat连接数
- /env:环境变量、配置信息
- /loggers:日志级别查看和动态修改
- /beans:容器所有Bean
- /mappings:所有URL映射
- /threaddump:线程快照
- /heapdump:堆内存快照(JVM分析用)
安全策略:
- 暴露控制:
management:
endpoints:
web:
exposure:
include: health,info,metrics # 只暴露必要端点
endpoint:
health:
show-details: when_authorized # 详情只给授权用户- 访问控制:
spring:
security:
user:
name: admin
password: ${ADMIN_PASSWORD}
management:
endpoints:
web:
base-path: /actuator- 网络隔离:
- Actuator端口与应用端口分离:management.server.port=8081
- 内网访问,公网禁止:防火墙规则或VPN
- 监控集成:
- 接入Prometheus:micrometer-registry-prometheus
- Grafana可视化:JVM、Tomcat、HTTP指标看板
- 告警规则:内存>80%、错误率>5%触发告警
生产红线:禁止公网暴露/env、/heapdump、/threaddump,这些端点包含敏感信息,可能被攻击者利用。建议只暴露/health给负载均衡器探测。