听诊器怎么用Spring Boot面试30题:3年经验内被追问最狠的陷阱题

新闻资讯2026-04-21 07:14:39

去年校招季,某大厂后端组面了127个候选人,最终挂掉的人里,有83%倒在同一类问题上——不是算法,不是八股,是Spring Boot那些"看起来简单,细想就懵"的机制题。

面试官的原话是:「能跑通CRUD的人太多了,我们要的是知道为什么能跑通的人。」

这份30题清单来自我整理过去18个月的真实面经,覆盖0-3年经验区间被追问频率最高的考点。不堆砌概念,只捞那些一答就露馅的盲区。

自动配置:Spring Boot的"自动驾驶"到底怎么判定的

第一梯队问题永远绕不开@SpringBootApplication。三个注解合三为一只是表象,真正挖坑的是@EnableAutoConfiguration的加载机制。

面试现场的经典追问链:自动配置类怎么被发现的?→ META-INF/spring.factories(Spring Boot 2.7+改为META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)→ 条件注解怎么生效?→ @ConditionalOnClass@ConditionalOnProperty的判定时机。

候选人最常翻车的点:把自动配置和组件扫描混为一谈。 自动配置是"根据类路径推断该装什么",组件扫描是"按包路径找到你写的类"。一个靠spring.factories,一个靠@ComponentScan,启动阶段执行的逻辑完全不同。

有个细节能区分背题党和理解党:问spring.factories加载的优先级。答案是Bootstrap ClassLoader层级加载,意味着你自己定义的同名配置类不会覆盖jar包里的——除非你显式用@AutoConfigureBefore/@AutoConfigureAfter干预。

Starter依赖:为什么加一行配置就能用Redis

Starter的魔法被过度神话了。真相是:它只是个传递依赖(Transitive Dependency)的打包器,加上一点自动配置的触发逻辑。

spring-boot-starter-data-redispom.xml里实际干了三件事:引入lettuce-corejedis、引入spring-data-redis、引入spring-boot-starter基础包。自动配置类RedisAutoConfiguration则负责在检测到RedisOperations类存在时,根据application.properties里的spring.redis.*参数拼装连接池。

追问陷阱:如果公司内网Maven仓库没有外网权限,怎么排查Starter引入的依赖版本冲突?正确路径是mvn dependency:tree看传递树,而不是盲目改版本号。

另一个高频盲区:自定义Starter。正确姿势是新建模块→src/main/resources/META-INF/spring.factories里注册你的自动配置类→打包安装到私服。但90%的候选人说不清spring.factories的key为什么要用org.springframework.boot.autoconfigure.EnableAutoConfiguration这个全限定名。

内嵌容器:Tomcat是怎么被"嵌"进去的

Spring Boot可执行jar的启动流程是二段式:先由JarLauncher加载嵌套jar的类路径,再委托给SpringApplication.run。内嵌容器的启动藏在ServletWebServerApplicationContextonRefresh阶段——这是ApplicationContext生命周期的一个钩子。

关键类TomcatServletWebServerFactorygetWebServer方法会实例化Tomcat对象,配置Connector、Host、Context,最后调用tomcat.start()。整个过程没有web.xml,全靠ServletContextInitializer接口的实现类(比如DispatcherServlet的注册)完成Servlet 3.0+规范的程序化配置。

一个能加分的冷知识:内嵌Tomcat默认只启动一个Connector,端口8080。 但你可以通过定义多个TomcatServletWebServerFactory Bean或者直接用WebServerFactoryCustomizer来搞多端口——虽然生产环境没人这么干,但能说出WebServerFactoryCustomizer接口的候选人,面试官会默认你对启动流程有源码级了解。

切换容器也是常考题:spring-boot-starter-web排除Tomcat、引入Jetty或Undertow的依赖即可。但Jetty的线程模型和Tomcat的NIO差异、Undertow的XNI(扩展NIO)设计,属于3年经验里"知道更好,不知道也不扣分"的延伸区。

Actuator:生产环境的"听诊器"怎么用不翻车

/actuator/health默认只暴露status: "UP",但加个配置就能展开详情:management.endpoint.health.show-details=always。K8s的livenessProbe和readinessProbe就是读这个端点,但很多人配完就忘关——测试环境无所谓,生产环境把数据库连接池详情暴露出去,等于给攻击者送情报。

端点暴露的粒度控制是安全面试的联动考点:management.endpoints.web.exposure.include白名单、exclude黑名单、jmxweb的分离配置。Spring Boot 2.x之后/actuator路径默认不暴露任何端点,必须显式开启,这个改动就是为了防默认裸奔。

自定义Health Indicator是实操加分项:实现HealthIndicator接口,返回Health.up().withDetail("customMetric", value).build(),就能让/actuator/health带上你的业务健康检查——比如第三方API的连通性、缓存命中率阈值。

Metrics和Prometheus的集成现在几乎是标配:micrometer-registry-prometheus依赖一加,/actuator/prometheus端点直接输出格式化的时序数据。但候选人经常被问懵:Micrometer的TimerCounter区别是什么?答案是Timer自带百分位统计(P50/P95/P99),Counter只是单调递增。

配置加载:15种配置源的优先级你排得清吗

Spring Boot的配置优先级是倒金字塔结构,官方文档列了17种(不同版本有增减),但面试能说出前8种已经跑赢大多数人。核心顺序:命令行参数 > SPRING_APPLICATION_JSON环境变量 > ServletConfig初始化参数 > ServletContext初始化参数 > JNDI属性 > Java系统属性(System.getProperties()) > 操作系统环境变量 > application-{profile}.properties

最隐蔽的坑:@PropertySource加载的自定义properties文件,优先级低于application.properties 很多人以为@PropertySource("classpath:custom.properties")能覆盖默认配置,结果发现不生效——因为ConfigFileApplicationListener的执行时机晚于PropertySource的处理。

Profile-specific配置的加载时机也是高频盲区:application-dev.ymlapplication-prod.yml不是运行时二选一,而是先加载默认配置,再用Profile配置覆盖。这意味着如果application.yml里有个默认值,application-dev.yml没显式声明,最终用的还是默认值——不是"空值",是"默认值"。

配置加密的实操方案:Jasypt是老派选择,Spring Cloud Config Server + Vault是云原生路线。但0-3年经验区间,能说出EnvironmentPostProcessor接口、自定义配置解析逻辑的候选人,已经属于"超预期"档位。

这题堪称Spring Boot面试的"八股之王",但年年有人踩同样的坑。失效场景清单:同类方法调用(this指针绕过代理)、非public方法、异常被catch没抛、rollbackFor配置错误、异步方法(@Async)、多数据源没指定TransactionManager、传播行为设置不当、final方法(CGLIB代理限制)。

同类调用失效的原理:Spring的事务代理是基于AOP的,而AOP代理是替换掉Spring容器里的Bean引用。方法内部this.methodB()用的是原生对象,不是代理对象,自然绕过了事务拦截器。解决方案:注入自身代理(@Autowired自己)、或者用AopContext.currentProxy()(需要开启expose-proxy=true)。

传播行为的面试考点集中在REQUIRED(默认,加入已有事务)、REQUIRES_NEW(挂起当前,开新事务)、NESTED(保存点,可部分回滚)。能画出事务边界图、说明物理事务和逻辑事务区别的候选人,面试官会默认你有分布式事务的预备知识。

事务超时和只读属性是性能优化的隐藏开关:@Transactional(timeout = 5, readOnly = true)能让某些ORM框架(如Hibernate)跳过脏检查,但MyBatis对此无感知——这是框架差异导致的"配置生效但效果不同"陷阱。

最后5题:那些"以为不会考"的边角料

Spring Boot的日志体系:默认SLF4J + Logback,但spring-boot-starter-logging可以被排除、替换为Log4j2。关键配置logging.level.root=WARNlogging.pattern.console的占位符语法(%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n)。

DevTools的热部署原理:两个ClassLoader,一个加载不变的三方jar,一个加载业务类。重启时只重建业务ClassLoader,所以"快"。但和JRebel的字节码替换不是一回事,生产环境必须剔除DevTools依赖。

Spring Boot 2.x到3.x的迁移成本: Jakarta EE命名空间(javax.*jakarta.*)、Java 17基线、GraalVM原生镜像支持。现在面试问"你用过Spring Boot 3吗",没迁移过项目的候选人可以诚实说"了解差异,公司项目还在2.7 LTS",但说不出Jakarta改名这件事会显得跟进不足。

SpringApplication.run的返回值是ConfigurableApplicationContext,不是很多人以为的ApplicationContext。这个接口继承了Lifecycle,意味着你可以程序化控制容器的启动停止——虽然99%的人用不到。

最后一个是冷到不能再冷的:banner.txt支持变量占位符(${spring-boot.version}${AnsiColor.BRIGHT_RED}),而且可以通过SpringApplication.setBanner()程序化定制。面试时提一嘴,面试官的表情通常是"这你都看过源码?"。

这份清单的30题完整版我按出现频率排了序,前10题在80%的面试里至少出现3道。你现在能不看答案,流畅说出自动配置的类加载顺序、事务代理的CGLIB vs JDK动态代理选择逻辑、以及Actuator端点的安全加固方案吗?