Skip to content

本轮 SpringBoot2核心功能面试题库(带答案)

【面试题】1. SpringBoot中@Import注解有哪三种导入方式?分别适用于什么场景? ​

【标准答案】三种方式:

  1. 直接导入类:@Import(A.class),直接将A注册为Bean,不需要A加@Component等注解。适合导入第三方jar包里的类,因为没法往别人代码加注解。
  2. 导入ImportSelector实现类:@Import(MySelector.class),MySelector实现ImportSelector接口,在selectImports方法里返回要导入的全类名数组。适合条件化批量导入,比如@EnableAutoConfiguration就是用这种方式,根据spring.factories动态决定导入哪些自动配置类。
  3. 导入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的超集? ​

【标准答案】核心区别:

  1. BeanFactory是Spring最底层的IoC容器接口,提供最基本的Bean管理功能(getBean、containsBean等),采用懒加载,只有真正获取Bean时才实例化。
  2. 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的完整生命周期回调顺序是什么?请按执行顺序列举。 ​

【标准答案】从实例化到销毁,完整回调顺序:

创建阶段:

  1. BeanDefinition注册到容器
  2. BeanFactoryPostProcessor处理BeanDefinition(修改属性值等)
  3. 实例化Bean(构造函数)
  4. 属性注入(@Autowired、@Value等)
  5. Aware接口回调:BeanNameAware → BeanFactoryAware → ApplicationContextAware
  6. BeanPostProcessor#postProcessBeforeInitialization
  7. @PostConstruct注解方法
  8. InitializingBean#afterPropertiesSet
  9. 自定义init-method
  10. 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需要哪些步骤?核心文件有哪些? ​

【标准答案】五步走:

  1. 创建Starter项目:普通Maven项目,命名规范xxx-spring-boot-starter(官方反着写spring-boot-starter-xxx),引入spring-boot-starter依赖。

  2. 编写自动配置类:用@Configuration + 条件注解控制Bean创建,例如:

java
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties props) {
        return new MyService(props.getPrefix(), props.getSuffix());
    }
}
  1. 编写配置属性类:@ConfigurationProperties + @Validated绑定配置,支持松散绑定。

  2. 注册自动配置:在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(SpringBoot 2.7+)或META-INF/spring.factories(旧版)中写入自动配置类的全路径。

  3. 编写默认配置:在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种方式(优先级从高到低):

  1. 命令行参数:--spring.profiles.active=prod
  2. JVM参数:-Dspring.profiles.active=prod
  3. 环境变量:SPRING_PROFILES_ACTIVE=prod
  4. application.yml中配置:spring.profiles.active: dev
  5. 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有区别吗? ​

【标准答案】五种通知类型及执行顺序:

正常执行流程:

  1. @Around(前半段,proceed()之前)
  2. @Before
  3. 目标方法执行
  4. @AfterReturning
  5. @After
  6. @Around(后半段,proceed()之后)

异常执行流程:

  1. @Around(前半段)
  2. @Before
  3. 目标方法抛异常
  4. @AfterThrowing
  5. @After
  6. @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生命周期顺序:

  1. ApplicationStartingEvent:应用刚启动,还没做任何处理时发布
  2. ApplicationEnvironmentPreparedEvent:Environment准备好,上下文还没创建
  3. ApplicationContextInitializedEvent:上下文初始化完成,Bean定义还没加载(2.1+新增)
  4. ApplicationPreparedEvent:Bean定义加载完成,还没refresh
  5. ContextRefreshedEvent:上下文refresh完成,所有Bean初始化完毕
  6. ContextStartedEvent:调用ConfigurableApplicationContext#start()时发布
  7. ApplicationReadyEvent:应用完全启动,可以接收请求
  8. 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

具体规则:

  1. 配置文件中用kebab-case(短横线):my-property → 映射到myProperty
  2. 下划线形式:my_property → 映射到myProperty
  3. 全大写+下划线:MY_PROPERTY → 映射到myProperty(环境变量常用)
  4. 驼峰:myProperty → 直接匹配

不支持的反向映射:Java字段名firstName不能在配置文件写firstname(没有分隔符无法推断边界)。

使用限制:松散绑定只适用于@ConfigurationProperties,@Value注解不支持松散绑定,必须精确匹配。

@ConstructorBinding:SpringBoot 2.2+支持构造器绑定,配合@ConfigurationProperties使用不可变对象,更安全。需要在配置类上加@ConfigurationPropertiesScan或@EnableConfigurationProperties。

【面试题】10. SpringBoot如何实现优雅停机?底层原理是什么? ​

【标准答案】SpringBoot 2.3+原生支持优雅停机,配置:

yaml
server:
  shutdown: graceful  # 开启优雅停机,默认immediate
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 最多等待30秒

底层原理:

  1. 收到SIGTERM信号(kill命令)或调用shutdown端点
  2. ApplicationContext发布ContextClosedEvent,触发SmartLifecycle#stop()
  3. Web容器停止接收新请求(Tomcat把Connector置为非accept状态)
  4. 等待已接收的请求处理完成(在timeout时间内)
  5. 超时后强制关闭,销毁所有Bean(@PreDestroy → DisposableBean → destroy-method)
  6. 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,给容器足够时间完成优雅停机。