本轮 SpringBoot2核心功能面试题库(带答案)
【面试题】1. SpringBoot中@Import注解有哪三种导入方式?分别适用于什么场景?
【标准答案】三种方式:
- 直接导入类:
@Import(A.class),直接将A注册为Bean,不需要A加@Component等注解。适合导入第三方jar包里的类,因为没法往别人代码加注解。 - 导入ImportSelector实现类:
@Import(MySelector.class),MySelector实现ImportSelector接口,在selectImports方法里返回要导入的全类名数组。适合条件化批量导入,比如@EnableAutoConfiguration就是用这种方式,根据spring.factories动态决定导入哪些自动配置类。 - 导入ImportBeanDefinitionRegistrar实现类:
@Import(MyRegistrar.class),直接操作BeanDefinitionRegistry手动注册BeanDefinition。灵活度最高,适合需要精细控制Bean注册过程的场景,比如MyBatis的@MapperScan就是用这种方式扫描Mapper接口并注册。
面试加分点:三种方式优先级从低到高——直接导入 < ImportSelector < ImportBeanDefinitionRegistrar,后者可以覆盖前者注册的同名Bean。
【面试题】2. SpringBoot自动配置的条件注解@ConditionalOnProperty是如何工作的?举例说明。
【标准答案】@ConditionalOnProperty根据Spring Environment中的属性值来决定是否创建Bean。 核心属性:
- prefix/value:属性前缀或完整属性名
- name:属性名
- havingValue:期望的属性值,只有属性值等于它才生效
- matchIfMissing:属性不存在时是否默认匹配,默认false
举例:@ConditionalOnProperty(prefix = "spring.datasource", name = "type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) 表示如果配置了spring.datasource.type等于HikariDataSource就生效,没配也生效(matchIfMissing=true),所以HikariCP是SpringBoot默认连接池。
另一个常见场景:@ConditionalOnProperty(name = "spring.redis.enabled", havingValue = "true") 控制Redis配置是否生效,没配或配了false都不生效。
注意:havingValue是精确匹配,不是包含匹配。属性值必须完全等于havingValue才算匹配。
【面试题】3. BeanFactory和ApplicationContext有什么区别?为什么说ApplicationContext是BeanFactory的超集?
【标准答案】核心区别:
- BeanFactory是Spring最底层的IoC容器接口,提供最基本的Bean管理功能(getBean、containsBean等),采用懒加载,只有真正获取Bean时才实例化。
- ApplicationContext是BeanFactory的子接口,在BeanFactory基础上扩展了大量企业级功能:
- 国际化支持(MessageSource)
- 事件发布机制(ApplicationEventPublisher)
- 资源加载(ResourceLoader,支持classpath:、file:等前缀)
- Environment集成(获取配置属性)
- AOP集成(自动识别@Aspect等)
- Bean生命周期管理(预实例化所有单例Bean)
实际开发中用的都是ApplicationContext(具体实现如AnnotationConfigApplicationContext),BeanFactory基本只在框架内部使用。SpringBoot启动时创建的就是ApplicationContext,它会在refresh阶段预先实例化所有单例Bean,而不是懒加载。
面试关键点:ApplicationContext继承自BeanFactory,功能上是超集;但注意ApplicationContext的getBean等方法行为和BeanFactory一致,区别在于附加功能。
【面试题】4. SpringBoot中Bean的完整生命周期回调顺序是什么?请按执行顺序列举。
【标准答案】从实例化到销毁,完整回调顺序:
创建阶段:
- BeanDefinition注册到容器
- BeanFactoryPostProcessor处理BeanDefinition(修改属性值等)
- 实例化Bean(构造函数)
- 属性注入(@Autowired、@Value等)
- Aware接口回调:BeanNameAware → BeanFactoryAware → ApplicationContextAware
- BeanPostProcessor#postProcessBeforeInitialization
- @PostConstruct注解方法
- InitializingBean#afterPropertiesSet
- 自定义init-method
- BeanPostProcessor#postProcessAfterInitialization(AOP代理在此生成)
销毁阶段: 11. @PreDestroy注解方法 12. DisposableBean#destroy 13. 自定义destroy-method
面试技巧:记住三个维度——注解方式(@PostConstruct/@PreDestroy) → 接口方式(InitializingBean/DisposableBean) → XML/配置方式(init-method/destroy-method),同维度内注解优先于接口优先于配置。BeanPostProcessor横切在初始化前后,AOP代理在postProcessAfterInitialization中生成。
【面试题】5. 自定义一个SpringBoot Starter需要哪些步骤?核心文件有哪些?
【标准答案】五步走:
创建Starter项目:普通Maven项目,命名规范
xxx-spring-boot-starter(官方反着写spring-boot-starter-xxx),引入spring-boot-starter依赖。编写自动配置类:用@Configuration + 条件注解控制Bean创建,例如:
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyProperties props) {
return new MyService(props.getPrefix(), props.getSuffix());
}
}编写配置属性类:@ConfigurationProperties + @Validated绑定配置,支持松散绑定。
注册自动配置:在
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(SpringBoot 2.7+)或META-INF/spring.factories(旧版)中写入自动配置类的全路径。编写默认配置:在
META-INF/spring-configuration-metadata.json或用@ConfigurationProperties自动生成元数据,IDE会提示配置项。
核心文件清单:pom.xml、自动配置类、属性类、AutoConfiguration.imports(或spring.factories)、默认配置文件。
面试加分:强调命名规范区别——官方starter是spring-boot-starter-xxx,第三方是xxx-spring-boot-starter。另外2.7版本开始推荐用AutoConfiguration.imports替代spring.factories。
【面试题】6. SpringBoot的多环境Profile机制是如何实现的?激活Profile的方式有哪些?优先级如何?
【标准答案】Profile本质是一组条件化配置的集合,通过@Profile注解和配置文件实现环境隔离。
配置文件方式:创建application-{profile}.yml,如application-dev.yml、application-prod.yml。激活后,profile专属配置覆盖默认配置。
激活Profile的5种方式(优先级从高到低):
- 命令行参数:
--spring.profiles.active=prod - JVM参数:
-Dspring.profiles.active=prod - 环境变量:
SPRING_PROFILES_ACTIVE=prod - application.yml中配置:
spring.profiles.active: dev - SpringApplication代码设置:
app.setAdditionalProfiles("prod")
@Profile注解方式:标注在@Bean或@Configuration上,只有对应Profile激活时才生效。如@Profile("dev")标注的配置类只在dev环境生效。
优先级规则:profile专属配置 > 默认配置。多Profile同时激活时,后声明的覆盖先声明的。SpringBoot 2.4+引入了spring.config.activate.on-profile替代旧的spring.profiles,分组机制更清晰。
面试关键:Profile不只用于配置文件,还能配合@Profile控制Bean的条件装配,这是很多人忽略的。
【面试题】7. SpringBoot AOP中五种通知类型的执行顺序是什么?Spring5和Spring4有区别吗?
【标准答案】五种通知类型及执行顺序:
正常执行流程:
- @Around(前半段,proceed()之前)
- @Before
- 目标方法执行
- @AfterReturning
- @After
- @Around(后半段,proceed()之后)
异常执行流程:
- @Around(前半段)
- @Before
- 目标方法抛异常
- @AfterThrowing
- @After
- @Around后半段不执行(异常向上传播)
Spring4 vs Spring5的区别(重要!):
- Spring4中:@Before → @After → @AfterReturning/AfterThrowing
- Spring5中:@Before → @AfterReturning/AfterThrowing → @After
Spring5把@After移到了最后,更符合finally语义——AfterReturning/AfterThrowing对应try里的return/catch,@After对应finally,逻辑上更合理。
多个切面用@Order控制优先级,Order值越小优先级越高,前置通知先执行,后置通知后执行(洋葱模型:先进后出)。
【面试题】8. SpringBoot事件机制中,内置的核心事件有哪些?按触发顺序排列。
【标准答案】按ApplicationContext生命周期顺序:
- ApplicationStartingEvent:应用刚启动,还没做任何处理时发布
- ApplicationEnvironmentPreparedEvent:Environment准备好,上下文还没创建
- ApplicationContextInitializedEvent:上下文初始化完成,Bean定义还没加载(2.1+新增)
- ApplicationPreparedEvent:Bean定义加载完成,还没refresh
- ContextRefreshedEvent:上下文refresh完成,所有Bean初始化完毕
- ContextStartedEvent:调用ConfigurableApplicationContext#start()时发布
- ApplicationReadyEvent:应用完全启动,可以接收请求
- ContextClosedEvent:上下文关闭时发布
其中最常用的是ContextRefreshedEvent(容器刷新完成)和ApplicationReadyEvent(应用就绪)。注意ContextStartedEvent不是自动触发的,需要手动调用start()。
面试陷阱:ContextRefreshedEvent在Spring MVC项目中可能触发两次——一次是Root ApplicationContext,一次是Servlet ApplicationContext。如果只想处理一次,需要判断event.getApplicationContext().getParent() == null。
【面试题】9. @ConfigurationProperties的松散绑定规则是什么?支持哪些写法?
【标准答案】松散绑定(Relaxed Binding)允许配置文件中的属性名和Java字段名不完全一致,SpringBoot自动匹配。
支持的写法映射(以firstName字段为例):
- application.yml:
first-name、first_name、firstName、FIRST_NAME、first-name - 环境变量:
FIRST_NAME、FIRST-NAME
具体规则:
- 配置文件中用kebab-case(短横线):
my-property→ 映射到myProperty - 下划线形式:
my_property→ 映射到myProperty - 全大写+下划线:
MY_PROPERTY→ 映射到myProperty(环境变量常用) - 驼峰:
myProperty→ 直接匹配
不支持的反向映射:Java字段名firstName不能在配置文件写firstname(没有分隔符无法推断边界)。
使用限制:松散绑定只适用于@ConfigurationProperties,@Value注解不支持松散绑定,必须精确匹配。
@ConstructorBinding:SpringBoot 2.2+支持构造器绑定,配合@ConfigurationProperties使用不可变对象,更安全。需要在配置类上加@ConfigurationPropertiesScan或@EnableConfigurationProperties。
【面试题】10. SpringBoot如何实现优雅停机?底层原理是什么?
【标准答案】SpringBoot 2.3+原生支持优雅停机,配置:
server:
shutdown: graceful # 开启优雅停机,默认immediate
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 最多等待30秒底层原理:
- 收到SIGTERM信号(kill命令)或调用shutdown端点
- ApplicationContext发布ContextClosedEvent,触发SmartLifecycle#stop()
- Web容器停止接收新请求(Tomcat把Connector置为非accept状态)
- 等待已接收的请求处理完成(在timeout时间内)
- 超时后强制关闭,销毁所有Bean(@PreDestroy → DisposableBean → destroy-method)
- JVM退出
关键点:
- 只有kill(SIGTERM)触发优雅停机,kill -9(SIGKILL)无法拦截直接杀进程
- 内嵌Tomcat、Undertow、Jetty都支持
- SmartLifecycle接口的getPhase()返回值控制关闭顺序,值越大越先关闭
- 需要配合spring-boot-starter-actuator暴露shutdown端点:
management.endpoint.shutdown.enabled=true,然后POST请求/actuator/shutdown
生产建议:K8s环境中配置terminationGracePeriodSeconds大于timeout-per-shutdown-phase,给容器足够时间完成优雅停机。