Skip to content

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

【面试题】1. SpringBoot启动类上的@SpringBootApplication注解由哪些注解组合而成?各自作用是什么? ​

【标准答案】@SpringBootApplication是一个组合注解,由三个核心注解构成:

  1. @SpringBootConfiguration:本质就是@Configuration,标识这是一个配置类,允许在类中定义@Bean方法
  2. @EnableAutoConfiguration:开启自动配置的核心,通过@Import导入AutoConfigurationImportSelector,利用类加载器读取META-INF/spring.factories(老版本)或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(新版本)中的自动配置类全限定名,结合条件注解按需装配
  3. @ComponentScan:开启组件扫描,默认扫描当前包及其子包下带有@Component/@Service/@Repository/@Controller/@Configuration等注解的类

实际开发中,如果需要自定义扫描路径,可以手动拆分这三个注解,指定@ComponentScan的basePackages。

【面试题】2. SpringBoot的条件注解@ConditionalOnProperty是如何工作的?请举例说明实际应用场景。 ​

【标准答案】@ConditionalOnProperty通过检查配置文件中的属性值来决定是否装配Bean。

核心属性:

  • prefix:属性前缀
  • name:属性名
  • havingValue:期望值
  • matchIfMissing:当属性不存在时是否匹配(默认false)

工作原理:容器启动时,从Environment中读取指定属性,判断是否符合条件,符合则注册Bean,否则跳过。

实际应用场景:

java
@Bean
@ConditionalOnProperty(prefix = "cache", name = "type", havingValue = "redis")
public CacheManager redisCacheManager() {
    return new RedisCacheManager();
}

@Bean
@ConditionalOnProperty(prefix = "cache", name = "type", havingValue = "ehcache", matchIfMissing = true)
public CacheManager ehCacheManager() {
    return new EhCacheCacheManager();
}

这样可以通过配置cache.type=redis切换缓存实现,不配置则默认使用Ehcache。

【面试题】3. SpringBoot的配置文件加载顺序和优先级规则是什么? ​

【标准答案】SpringBoot配置加载遵循"外层覆盖内层、后者覆盖前者"原则,优先级从高到低:

  1. 命令行参数(--server.port=8081)
  2. 系统属性(System.getProperties())
  3. 操作系统环境变量
  4. jar包外部的application-{profile}.properties/yml
  5. jar包内部的application-{profile}.properties/yml
  6. jar包外部的application.properties/yml
  7. jar包内部的application.properties/yml
  8. @Configuration类上的@PropertySource
  9. 默认属性(SpringApplication.setDefaultProperties)

实际开发中:

  • 开发环境用application-dev.yml配置本地数据库
  • 生产环境打包时,在jar同级目录放application-prod.yml,启动命令加--spring.profiles.active=prod
  • 敏感配置(数据库密码)用环境变量注入,避免硬编码

【面试题】4. @ConfigurationProperties与@Value注解绑定配置有什么区别?各有什么优缺点? ​

【标准答案】

@ConfigurationProperties优势:

  • 批量绑定:一次性将配置文件中的多个属性映射到Bean字段
  • 松散绑定:支持firstName、first-name、first_name、FIRST_NAME等格式自动映射
  • 复杂类型:支持Map、List、嵌套对象绑定
  • 属性校验:配合@Validated + @NotNull/@Min等JSR-303注解做校验
  • 配置提示:配合spring-boot-configuration-processor生成元数据,IDE有提示

@Value优势:

  • 简单直接:单个属性注入,代码量少
  • SpEL支持:支持表达式#{...},可以做复杂计算
  • 动态性:可以在非Spring管理的类中使用(需配合@PropertySource)

选择建议:

  • 配置项多、需要分组管理、需要校验 → 用@ConfigurationProperties
  • 单个属性、简单场景、需要SpEL计算 → 用@Value

实际项目中,数据库配置、Redis配置等复杂配置推荐@ConfigurationProperties,端口号、开关类配置用@Value即可。

【面试题】5. BeanPostProcessor和BeanFactoryPostProcessor有什么区别?各自的执行时机是什么? ​

【标准答案】

BeanFactoryPostProcessor:

  • 作用:在BeanDefinition加载完成后、Bean实例化之前,对BeanDefinition进行修改
  • 执行时机:ApplicationContext.refresh()的invokeBeanFactoryPostProcessors阶段,早于Bean实例化
  • 典型应用:PropertySourcesPlaceholderConfigurer处理${...}占位符,修改Bean的scope、lazy-init等属性
  • 注意:如果过早实例化Bean,会破坏BeanPostProcessor的拦截机制

BeanPostProcessor:

  • 作用:在Bean实例化后、初始化前后介入,对Bean进行增强或代理
  • 执行时机:Bean实例化完成后,在属性注入之后,调用初始化方法前后
  • 核心方法:
    • postProcessBeforeInitialization:@PostConstruct、InitializingBean之前执行
    • postProcessAfterInitialization:返回代理对象,如AOP、事务代理
  • 典型应用:AutowiredAnnotationBeanPostProcessor处理@Autowired,AbstractAutoProxyCreator创建AOP代理

面试回答时强调:BeanFactoryPostProcessor操作的是BeanDefinition(类定义),BeanPostProcessor操作的是Bean实例(对象)。

【面试题】6. @Import注解有哪几种使用方式?分别适用于什么场景? ​

【标准答案】@Import有三种导入方式:

方式一:直接导入配置类

java
@Import({MyConfiguration.class})

适用场景:需要引入其他配置类,又不想用@ComponentScan扫描整个包

方式二:实现ImportSelector接口

java
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        return new String[]{"com.example.MyConfiguration"};
    }
}

适用场景:根据条件动态决定导入哪些类,如@EnableAutoConfiguration的底层实现

方式三:实现ImportBeanDefinitionRegistrar接口

java
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class).getBeanDefinition();
        registry.registerBeanDefinition("myBean", bd);
    }
}

适用场景:需要手动注册BeanDefinition、动态修改Bean属性,如MyBatis的@MapperScan底层就是用这种方式注册Mapper

实际开发中,自定义Starter常用方式二和方式三实现自动装配。

【面试题】7. Spring AOP中JDK动态代理和CGLIB代理有什么区别?SpringBoot默认使用哪种? ​

【标准答案】

JDK动态代理:

  • 基于接口:目标类必须实现接口
  • 原理:通过反射生成实现接口的代理类
  • 性能:JDK8以后性能优化,接近CGLIB
  • 限制:只能代理接口方法

CGLIB代理:

  • 基于继承:通过字节码生成目标类的子类
  • 原理:使用ASM字节码框架生成代理类
  • 性能:创建代理慢,执行快(方法调用时少了反射开销)
  • 限制:无法代理final类和final方法

SpringBoot默认策略:

  • SpringBoot 2.x默认使用CGLIB代理(spring.aop.proxy-target-class默认true)
  • 原因:很多场景没有接口,CGLIB更通用

强制指定方式:

properties
spring.aop.proxy-target-class=false  # 强制使用JDK代理

面试注意点:事务失效场景之一——类内部调用不走代理,解决方案是自注入或AopContext.currentProxy()。

【面试题】8. SpringBoot的事件机制有哪些核心事件?实际开发中如何使用? ​

【标准答案】

核心内置事件(按启动顺序):

  1. ApplicationStartingEvent:启动开始,Environment未创建
  2. ApplicationEnvironmentPreparedEvent:Environment准备好
  3. ApplicationContextInitializedEvent:ApplicationContext初始化完成
  4. ApplicationPreparedEvent:BeanDefinition加载完成
  5. ContextRefreshedEvent:容器刷新完成,所有Bean已就绪
  6. ContextStartedEvent:调用start()方法时触发
  7. ApplicationReadyEvent:应用就绪,可以接收请求
  8. ContextClosedEvent:容器关闭时触发

自定义事件使用:

java
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private Long orderId;
    public OrderCreatedEvent(Object source, Long orderId) {
        super(source);
        this.orderId = orderId;
    }
}

// 2. 发布事件
@Autowired
private ApplicationEventPublisher publisher;
publisher.publishEvent(new OrderCreatedEvent(this, 123L));

// 3. 监听事件
@Component
public class OrderEventListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 发送通知、记录日志等
    }
}

异步事件:

java
@EventListener
@Async
public void onOrderCreated(OrderCreatedEvent event) {
    // 异步处理,不阻塞主线程
}

实际应用:订单创建后发送消息通知、用户注册后初始化数据、系统启动后加载缓存。

【面试题】9. SpringBoot如何切换嵌入式容器?Tomcat、Undertow、Jetty各自有什么特点? ​

【标准答案】

切换方式(Maven):

xml
<!-- 默认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>

三大容器对比:

容器特点适用场景
Tomcat生态最成熟、文档丰富、社区活跃通用Web应用、传统企业系统
Undertow轻量级、高并发、内存占用低高性能网关、微服务、I/O密集型
Jetty低内存、易嵌入、长连接支持好聊天服务器、推送服务、嵌入式设备

性能调优示例(Undertow):

yaml
server:
  undertow:
    io-threads: 16
    worker-threads: 256
    buffer-size: 1024
    direct-buffers: true

面试建议:简单应用用默认Tomcat,高并发场景考虑Undertow。

【面试题】10. @Transactional注解失效的常见场景有哪些?如何避免? ​

【标准答案】

常见失效场景:

  1. 类内部调用:同一个类中方法A调用方法B,B的事务不生效

    • 原因:不走代理,直接调用目标对象方法
    • 解决:自注入自己,或用AopContext.currentProxy()
  2. 方法非public:protected/private方法加@Transactional无效

    • 原因:Spring AOP默认只代理public方法
    • 解决:改为public,或开启AspectJ编译时织入
  3. 异常被捕获:try-catch捕获异常后未抛出

    • 原因:代理类检测不到异常,认为方法执行成功
    • 解决:手动抛出异常,或设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
  4. 异常类型不匹配:默认只回滚RuntimeException和Error

    • 原因:checked异常不回滚
    • 解决:@Transactional(rollbackFor = Exception.class)
  5. 传播行为配置错误:REQUIRES_NEW嵌套调用导致死锁或数据不一致

    • 解决:理解7种传播行为,合理选择REQUIRED/REQUIRES_NEW
  6. 多数据源场景:事务管理器未正确绑定数据源

    • 解决:@Transactional(transactionManager = "secondaryTransactionManager")

最佳实践:

  • 事务方法尽量放在Service层,不要嵌套调用
  • 统一使用rollbackFor = Exception.class
  • 避免事务方法耗时操作(远程调用、文件IO),防止长事务