Skip to content

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

【面试题】1. SpringBoot自动配置类上@ConditionalOnBean和@ConditionalOnMissingBean同时存在时,判断逻辑是怎样的?为什么自动配置类中大量使用@ConditionalOnMissingBean而非@ConditionalOnBean? ​

【标准答案】这两个注解同时存在时是AND关系——必须同时满足才生效。自动配置源码中大量用@ConditionalOnMissingBean是因为它的设计理念是"用户优先":如果用户已经自己定义了同类型的Bean,自动配置就不应该覆盖用户的配置,而是退让。比如DataSourceAutoConfiguration里配HikariDataSource,加了@ConditionalOnMissingBean(DataSource.class),意思就是"你没有数据源我才帮你建一个,有了我就不插手"。反过来如果用@ConditionalOnBean,那必须容器里已有某个Bean才装配,这在自动配置场景里逻辑是反的——自动配置本身就是来补充缺失Bean的,不是来依赖已有Bean的。另外@ConditionalOnBean容易导致循环依赖和顺序问题,因为依赖的Bean可能还没注册,判断结果不可控。

【面试题】2. SpringBoot的外部化配置源有哪些?按照优先级从高到低说出前5个,并说明命令行参数为什么优先级最高? ​

【标准答案】外部化配置源优先级从高到低:1)命令行参数(--server.port=8081);2)JNDI属性;3)Java系统属性(System.getProperties());4)操作系统环境变量;5)application-{profile}.yml/properties(jar包外)。命令行参数优先级最高是因为它是在应用启动时最后传入的覆盖值,设计初衷就是让运维人员在部署时能灵活覆盖一切配置,不需要改代码或重新打包。底层实现上,SpringBoot在SpringApplication.run()中会把命令行参数封装成SimpleCommandLinePropertySource,并插入到Environment的PropertySource列表的最前面,而PropertySources的查找逻辑是"找到第一个就返回",所以排在前面的命令行参数永远赢。

【面试题】3. FactoryBean和BeanFactory有什么区别?SpringBoot中哪些地方用到了FactoryBean? ​

【标准答案】这是两个完全不同的东西。BeanFactory是IoC容器的根接口,负责管理Bean的整个生命周期,是容器本身;FactoryBean是一种特殊的工厂Bean,用来创建复杂对象,getObject()返回的才是真正放入容器的Bean。区别:BeanFactory.getBean("myFactory")拿到的是FactoryBean.getObject()返回的对象,如果要拿FactoryBean本身得加前缀&:BeanFactory.getBean("&myFactory")。SpringBoot中大量使用FactoryBean,比如MyBatis的MapperFactoryBean(为每个Mapper接口创建代理对象)、Spring Integration的MessageChannelFactoryBean、RabbitMQ的SimpleMessageListenerContainerFactoryBean等。面试中核心要表达的就是:FactoryBean是"生产Bean的Bean",是对复杂对象创建过程的封装。

【面试题】4. @ComponentScan的扫描规则是什么?为什么SpringBoot启动类要放在根包下?如果不放在根包下怎么解决? ​

【标准答案】@ComponentScan默认扫描当前包及其所有子包。SpringBoot启动类放在根包下,是因为@SpringBootApplication里包含的@ComponentScan没有显式指定basePackages,就默认以启动类所在包为根,这样项目里所有@Component/@Service/@Repository/@Controller都能被扫到。如果不放根包下,有两种解决方案:1)在启动类上显式指定@ComponentScan(basePackages = "com.example");2)用@SpringBootApplication的scanBasePackages属性指定。面试里要强调的是:这不是强制规定,是约定优于配置的体现——按约定放就不用额外配置,不按约定就得手动指定。另外SpringBoot的自动配置类不走@ComponentScan,它们是通过spring.factories/AutoConfiguration.imports机制加载的,这是两套体系。

【面试题】5. Spring AOP中同一个类里方法A调用方法B,B上的切面逻辑会生效吗?为什么?怎么解决? ​

【标准答案】不会生效。原因:Spring AOP是基于代理的,外部调用方法A时走的是代理对象,代理会执行切面逻辑;但A内部调用this.B()时,this指向的是目标对象本身而不是代理对象,所以B上的切面逻辑被跳过了。这是典型的"自调用失效"问题。解决方案:1)注入自身(@Lazy private XxxService self; 然后 self.B()),让调用走代理;2)从ApplicationContext中获取代理对象再调用;3)用AopContext.currentProxy()获取当前代理对象(需开启@EnableAspectJAutoProxy(exposeProxy=true));4)把方法B抽到另一个Service里,避免自调用。生产环境推荐方案3或4,方案1虽然简单但看起来有点怪。

【面试题】6. SpringBoot事件机制中,@EventListener标注的方法在什么情况下会执行失败?如果事件监听器抛异常,会不会影响事件发布者? ​

【标准答案】默认情况下@EventListener是同步执行的,所以监听器抛异常会直接向上传播到事件发布者,导致发布者也失败。常见执行失败场景:1)监听器抛异常导致发布者事务回滚——这是最坑的,一个不相关的监听器异常把业务主流程搞挂了;2)监听器方法参数类型和事件类型不匹配;3)@Async异步监听器但没配线程池,用的SimpleAsyncTaskExecutor无限创建线程。解决方案:1)用@Async让监听器异步执行,异常不会影响发布者;2)用@TransactionalEventListener(phase = AFTER_COMMIT),在事务提交后才执行,避免监听器异常回滚主事务;3)异步监听器配自定义线程池+异常处理器。面试重点:区分@EventListener和@TransactionalEventListener的事务边界。

【面试题】7. SpringBoot嵌入式Tomcat的自动配置入口在哪里?如果要禁用Tomcat换Undertow,除了排除依赖还需要注意什么? ​

【标准答案】自动配置入口是ServletWebServerFactoryAutoConfiguration,它会根据类路径上的容器类型来决定创建哪个WebServerFactory:有Tomcat类就创建TomcatServletWebServerFactory,有Undertow类就创建UndertowServletWebServerFactory。切换步骤:1)排除spring-boot-starter-tomcat依赖(在spring-boot-starter-web里用<exclusions>排除);2)添加spring-boot-starter-undertow依赖;3)注意Undertow默认不支持JSP,如果项目用了JSP就不能换;4)Undertow的配置前缀是server.undertow.(如server.undertow.io-threads、server.undertow.worker-threads),和Tomcat的server.tomcat.不同,需要调整参数;5)如果用了WebSocket,Undertow需要额外引入undertow-websockets-jsr依赖。面试加分点:Undertow是NIO架构,内存占用更小,适合高并发长连接场景;Tomcat生态成熟、文档丰富,适合大多数业务场景。

【面试题】8. SpringBoot中@WebMvcTest和@SpringBootTest的区别是什么?什么时候该用哪个? ​

【标准答案】@SpringBootTest启动完整的应用上下文(包含所有Bean、嵌入式容器等),是集成测试;@WebMvcTest是切片测试,只加载Web层相关的Bean(Controller、ControllerAdvice、WebMvcConfigurer等),不加载Service、Repository等组件,Controller依赖的Service需要用@MockBean模拟。使用场景:@WebMvcTest用于快速测试Controller的请求映射、参数绑定、数据校验、异常处理等Web层逻辑,速度快因为不用启动整个应用;@SpringBootTest用于测试多组件协作的完整流程,比如Service调用Repository写入数据库再通过Controller返回。关键区别:@WebMvcTest自动配置MockMvc,不用启动真实HTTP服务器;@SpringBootTest可以配合@AutoConfigureMockMvc在完整上下文中使用MockMvc,也可以用TestRestTemplate发真实HTTP请求。面试要点:测试越窄越快,能用@WebMvcTest就别用@SpringBootTest。

【面试题】9. SpringBoot的spring-boot-maven-plugin打包出的可执行JAR,和普通mvn package打出来的JAR有什么区别?为什么不能直接用java -cp运行? ​

【标准答案】普通JAR只包含自己项目的class文件和META-INF,依赖JAR需要单独在classpath中提供。spring-boot-maven-plugin打出来的Fat JAR包含三层结构:1)项目自身的class在BOOT-INF/classes/;2)依赖JAR在BOOT-INF/lib/;3)org/springframework/boot/loader/目录下是SpringBoot自定义的类加载器。不能直接用java -cp运行是因为JDK的标准类加载器不认识BOOT-INF/这种目录结构,它不会自动去BOOT-INF/lib/下加载嵌套的JAR。SpringBoot的JarLauncher通过自定义的LaunchedURLClassLoader来加载BOOT-INF/下的所有内容,实现了"一个JAR包含一切"的独立运行能力。面试加分:SpringBoot 2.3+支持分层JAR(layers),用jar -Djarmode=layertools -jar app.jar list可以看到分层,Docker构建时可以按依赖层/应用层分别缓存,依赖不变时只重建应用层,大幅加速镜像构建。

【面试题】10. SpringBoot中@EnableScheduling定时任务默认是单线程执行的,如果某个任务执行时间超过间隔会怎样?如何设计一个生产级别的定时任务方案? ​

【标准答案】默认单线程下,如果任务执行时间超过间隔,下一次执行会等当前任务完成后立即开始(不会并发执行),实际执行间隔被拉长。如果多个@Scheduled任务,一个阻塞了,其他任务都排队等着,全部受影响。生产级方案:1)配置定时任务线程池:实现SchedulingConfigurer接口,重写configureTasks方法设置ThreadPoolTaskScheduler的poolSize;2)每个任务加@Async实现异步执行,配合自定义线程池;3)如果任务量更大,不用Spring内置调度,接入XXL-JOB或Elastic-Job等分布式调度框架,支持集群分片、失败重试、可视化管理和监控;4)注意@Scheduled的Cron表达式在应用重启后不会补执行错过的任务,如果有补执行需求要用quartz的misfire机制。面试核心:单线程是默认值不是限制,知道怎么配线程池是基本要求,知道什么时候该上分布式调度框架是加分项。