1. 面试场景还原:当谢飞机遇上Spring Boot八股文
"说说Spring Boot自动装配原理?"面试官推了推眼镜,谢飞机的手心开始冒汗。这个经典场景每天都在无数Java求职面试中上演。作为经历过50+场技术面试的面试官,我见过太多候选人像谢飞机一样,背熟了@SpringBootApplication注解却说不清EnableAutoConfiguration的底层逻辑。
1.1 自动配置的魔法揭秘
Spring Boot的自动装配本质上是通过条件化Bean注册实现的。当你在pom.xml引入spring-boot-starter-web依赖时:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Maven依赖树会传递引入spring-boot-autoconfigure.jar,其中的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件定义了所有自动配置类。以经典的DispatcherServlet自动配置为例:
java复制@AutoConfiguration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@EnableConfigurationProperties(WebMvcProperties.class)
public class DispatcherServletAutoConfiguration {
// 自动注册DispatcherServlet
}
关键点在于@Conditional系列注解:
- @ConditionalOnClass:类路径存在指定类时生效
- @ConditionalOnMissingBean:容器不存在该类型Bean时生效
- @ConditionalOnProperty:配置参数满足条件时生效
面试陷阱:很多候选人能背出自动配置流程,但被问到"如何覆盖自动配置的默认行为"时就卡壳。其实只需在自定义配置类中声明同名Bean即可。
1.2 Starter机制的三层封装
优秀的Java工程师需要理解Starter的封装层次:
- 基础层:spring-boot-starter(核心启动器)
- 功能层:spring-boot-starter-json(JSON支持)
- 技术栈层:spring-boot-starter-data-jpa(数据库访问)
以数据库访问为例,引入starter-data-jpa后:
- 自动配置HikariCP连接池
- 自动扫描@Entity类
- 自动创建JpaRepository实现
java复制// 面试常考:如何自定义Starter?
// 1. 创建autoconfigure模块
// 2. 编写配置类+条件注解
// 3. 在META-INF/spring下创建:
// - org.springframework.boot.autoconfigure.AutoConfiguration.imports
// - spring.factories(兼容旧版)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 微服务连环问:从理论到实战的跨越
当面试进入微服务环节,谢飞机的表情从紧张变成了茫然。面试官在白板上画出的架构图,往往成为压垮候选人的最后一根稻草。
2.1 服务注册发现实战
以Nacos为例,服务注册的核心流程:
java复制@SpringBootApplication
@EnableDiscoveryClient
public class OrderService {
public static void main(String[] args) {
SpringApplication.run(OrderService.class, args);
}
}
配置文件中需要指定:
yaml复制spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
group: ORDER_GROUP
常见面试问题:
- CAP理论中Nacos如何实现AP模式?
- 通过Distro协议实现节点间数据同步
- 临时实例使用心跳检测,持久实例使用Raft协议
- 服务发现时客户端如何负载均衡?
- Ribbon默认轮询策略
- 可自定义WeightedResponseTimeRule
2.2 熔断降级的三板斧
当面试官要求"画一下Sentinel的熔断策略"时,可以这样拆解:
| 策略类型 | 参数说明 | 适用场景 |
|---|---|---|
| 慢调用比例 | 最大RT、比例阈值 | 数据库查询 |
| 异常比例 | 统计时长、阈值 | 第三方接口 |
| 异常数 | 最小请求数 | 边缘服务 |
配置示例:
java复制@SentinelResource(
value = "getUser",
blockHandler = "handleFlowLimit",
fallback = "getUserFallback"
)
public User getUser(String id) {
// 业务逻辑
}
避坑指南:很多候选人混淆blockHandler和fallback的区别。前者处理流控规则触发的情况,后者处理业务异常。
3. 面试官的黑盒测试:场景题破解之道
"现在有个需求,要你在不改动老代码的情况下新增鉴权功能..."这类场景题考察的是实际工程能力。
3.1 过滤器 vs 拦截器实战
java复制// 过滤器方案(Servlet层面)
@WebFilter(urlPatterns = "/*")
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 校验Token逻辑
if(!checkToken(request)) {
((HttpServletResponse)response).sendError(403);
return;
}
chain.doFilter(request, response);
}
}
// 拦截器方案(Spring层面)
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return checkToken(request);
}
}
关键区别:
- 过滤器能处理静态资源
- 拦截器可以获取Controller方法元数据
- 执行顺序:Filter → Interceptor → AOP
3.2 分布式事务的折中方案
当被问到"订单服务和库存服务如何保证数据一致性"时,可以这样分层回答:
-
强一致性方案:
- Seata AT模式(性能损耗约30%)
- 适用金融支付场景
-
最终一致性方案:
java复制// 使用RocketMQ事务消息 @RestController public class OrderController { @Autowired private RocketMQTemplate rocketMQTemplate; @Transactional public void createOrder(OrderDTO dto) { // 1. 本地事务 orderMapper.insert(dto); // 2. 发送预备消息 rocketMQTemplate.sendMessageInTransaction( "order-topic", MessageBuilder.withPayload(dto).build(), null ); } } -
补偿方案:
- 定时任务校对
- 人工干预接口
4. 性能优化死亡问答:从JVM到SQL
"你们的系统出现OOM怎么排查?"这类问题需要体系化的排查思路。
4.1 内存泄漏排查四步法
-
现场保存:
bash复制
jmap -dump:live,format=b,file=heap.hprof <pid> -
初步分析:
bash复制
jhat heap.hprof -
MAT深度分析:
- 查看Dominator Tree
- 分析GC Roots引用链
-
代码修复:
- 注意静态集合类
- 检查未关闭的资源(如Redis连接)
4.2 SQL优化实战案例
慢查询优化前后对比:
sql复制-- 优化前(全表扫描)
SELECT * FROM orders WHERE DATE_FORMAT(create_time,'%Y-%m')='2023-01';
-- 优化后(索引扫描)
SELECT * FROM orders
WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-31 23:59:59';
索引失效的常见陷阱:
- 隐式类型转换(varchar字段传number)
- 函数操作字段(WHERE SUBSTRING(name,1,3)='abc')
- 最左前缀缺失(联合索引跳过第一列)
5. 幽默背后的严肃技术:那些容易翻车的知识点
在轻松的氛围中,面试官往往突然抛出致命问题。
5.1 HashMap夺命连环问
java复制// 问题1:resize()过程是怎样的?
final Node<K,V>[] resize() {
// 计算新容量
// 创建新数组
// 迁移数据(链表可能拆分成高低位链)
}
// 问题2:为什么长度是2的幂次?
static final int tableSizeFor(int cap) {
// 保证hash & (length-1)等效于取模运算
}
5.2 线程池参数动态调整
java复制ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程
5, // 最大线程
60, // 存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100) // 任务队列
);
// 动态调整(面试加分项)
executor.setCorePoolSize(4);
executor.setMaximumPoolSize(10);
重要细节:设置corePoolSize>maxPoolSize会抛IllegalArgumentException
面试最后,当被问到"你有什么问题要问我"时,可以这样展现技术深度:
"请问贵司的微服务监控体系是如何搭建的?是基于Prometheus+Grafana的自研方案,还是使用商业APM工具?"
