Java大厂面试实录:从Spring Boot到微服务架构,谢飞机的三轮闯关记(附技术详解)
Java大厂面试实录:从Spring Boot到微服务架构,谢飞机的三轮闯关记(附技术详解)
📍 场景:某互联网大厂面试间 👨💼 面试官:王总(技术总监,严肃认真) 👨💻 求职者:谢飞机(水货程序员,自信满满)
🎬 开场
王总:(翻看着简历,眉头微皱)谢飞机是吧?看你简历写了三年Java开发经验,那我们就直接开始技术面试吧。
谢飞机:(挺直腰板,自信一笑)王总您好!三年经验不敢说,但三年摸鱼经验还是有的...啊不,是三年实战经验!
王总:(面无表情)那我们开始第一轮,先聊聊Java基础和Spring框架。
📋 第一轮:Java基础与Spring框架(电商场景)
问题1:HashMap底层原理
王总:你们做电商系统,商品数据肯定要用到缓存。先说说HashMap的底层实现原理?
谢飞机:(眼睛一亮,这题我会!)这个简单!HashMap就是数组加链表,JDK1.8之后还加了红黑树。当链表长度超过8的时候,就会转换成红黑树,提高查询效率!
王总:(微微点头)不错,那为什么是8和6呢?
谢飞机:(愣了一下)呃...这个...可能是因为8比较吉利?六六大顺嘛...
王总:(无奈摇头)这是泊松分布概率决定的,链表长度达到8时转为红黑树的概率极低。继续下一个问题。
问题2:Spring Bean生命周期
王总:我们电商系统用Spring管理Bean,你说说Spring Bean的生命周期?
谢飞机:(自信)这个我知道!首先是实例化,然后属性注入,再调用init方法,最后使用完调用destroy方法!
王总:(追问)那BeanPostProcessor是在哪个阶段执行的?
谢飞机:(挠头)这个...应该是在...在...在属性注入之后?还是之前?反正就在那附近...
王总:(叹气)BeanPostProcessor是在初始化前后执行的,分为Before和After两个方法。
问题3:Spring Boot自动配置原理
王总:你们项目用的Spring Boot吧?说说自动配置原理?
谢飞机:(来劲了)这个我熟!就是那个@SpringBootApplication注解,里面有@EnableAutoConfiguration,然后会读取META-INF/spring.factories文件,加载配置类!
王总:(难得露出微笑)嗯,这个答得不错。那条件注解@Conditional了解吗?
谢飞机:(得意)知道知道!就是满足条件才加载Bean嘛,比如@ConditionalOnClass,类路径下有这个类才生效!
王总:(点头)可以,第一轮基础还算扎实。我们进入第二轮,聊聊并发和缓存。
📋 第二轮:并发编程与Redis缓存(秒杀场景)
问题4:线程池核心参数
王总:电商秒杀场景,并发量很高。你们怎么设计线程池?核心参数有哪些?
谢飞机:(开始含糊)线程池嘛...就是那个ThreadPoolExecutor,有corePoolSize、maxPoolSize...还有...那个...队列...
王总:(追问)拒绝策略呢?
谢飞机:(更含糊了)拒绝策略有四种... AbortPolicy是抛异常... CallerRunsPolicy是...是让调用者运行?还有两个我记不太清了...
王总:(皱眉)DiscardPolicy是丢弃任务,DiscardOldestPolicy是丢弃最老任务。继续。
问题5:Redis缓存一致性
王总:秒杀商品库存放Redis,怎么保证和数据库的一致性?
谢飞机:(试图蒙混过关)这个...就是先更新数据库,再删除缓存...或者用那个...延迟双删?
王总:(追问)延迟双删的具体流程是什么?
谢飞机:(越说越虚)就是...先删缓存...然后更新数据库...然后再删一次缓存...中间加个延迟...具体延迟多久...这个要看业务...
王总:(摇头)延迟时间一般设置为500ms-1s,要大于业务读写时间。还有分布式锁、消息队列异步更新等方案。
问题6:Redis分布式锁
王总:秒杀场景下,怎么用Redis实现分布式锁?
谢飞机:(开始胡扯)就是用setnx命令...设置一个key...然后...然后加个过期时间...防止死锁...
王总:(追问)如果业务执行时间超过过期时间怎么办?
谢飞机:(彻底懵了)这个...可以...可以续期?用那个...看门狗?
王总:(终于露出一丝满意)Redisson的WatchDog机制确实可以自动续期,默认30秒。这个问题算你答对一半。
📋 第三轮:微服务与消息队列(支付场景)
问题7:Dubbo服务调用流程
王总:你们支付系统用的Dubbo?说说服务调用流程?
谢飞机:(又开始含糊)就是...消费者...调用...提供者...中间有个...注册中心...Zookeeper...
王总:(追问)负载均衡策略有哪些?
谢飞机:(硬着头皮)有随机...轮询...还有...那个...一致性哈希?
王总:(点头)还有最少活跃数和权重配置。继续。
问题8:Kafka消息积压
王总:支付回调消息用Kafka,如果出现消息积压怎么处理?
谢飞机:(彻底慌了)这个...可以...增加消费者?或者...扩容?
王总:(追问)具体怎么扩容?
谢飞机:(语无伦次)就是...加机器...加Topic...加Partition...反正就是加...
王总:(叹气)增加Partition后要同步增加Consumer,一个Partition只能被一个Consumer消费。还可以临时创建更多Partition的Topic来快速消费。
问题9:分布式事务
王总:支付系统涉及多个服务,怎么保证分布式事务?
谢飞机:(开始背概念)有...2PC...3PC...TCC...还有那个...Seata...
王总:(追问)TCC的三阶段是什么?
谢飞机:(支支吾吾)Try...Confirm...Cancel...Try是...是尝试...Confirm是确认...Cancel是取消...
王总:(无奈)Try是资源预留,Confirm是真正执行,Cancel是释放资源。你们项目用的哪种方案?
谢飞机:(老实回答)我们...用的...最终一致性...消息队列...
王总:(点头)基于消息队列的最终一致性确实是常用方案。
🎬 结束语
王总:(合上简历)好了,今天的面试就到这里。你的基础还可以,但一些深入的技术细节还需要加强。我们会尽快通知你面试结果,你先回去等通知吧。
谢飞机:(站起来,鞠了一躬)谢谢王总!那个...等通知是等几天?
王总:(面无表情)三个工作日内。
谢飞机:(小声嘀咕)三个工作日...那就是下周一...那我周末可以...(被王总瞪了一眼)啊不,我周末继续学习!
王总:(嘴角微微上扬)学习是好事,走吧。
📚 技术详解(小白学习专区)
一、HashMap底层原理详解
1.1 数据结构
JDK1.7:数组 + 链表
JDK1.8:数组 + 链表 + 红黑树
1.2 为什么链表转红黑树的阈值是8?
数学原理:基于泊松分布概率论
- 在理想情况下(负载因子0.75),链表长度达到8的概率约为0.00000006
- 这个概率极低,所以绝大多数情况下不会触发树化
- 红黑树节点数是链表节点数的一半,但空间占用是2倍
- 当长度降到6时转回链表,避免频繁转换
1.3 业务场景:电商商品缓存
// 商品SKU缓存
Map<String, Product> skuCache = new HashMap<>(1024);
// 时间复杂度:O(1) 快速查找商品
二、Spring Bean生命周期详解
2.1 完整生命周期
1. 实例化(Instantiation)
2. 属性赋值(Populate)
3. 初始化(Initialization)
- 调用BeanNameAware.setBeanName()
- 调用BeanFactoryAware.setBeanFactory()
- 调用ApplicationContextAware.setApplicationContext()
- 调用BeanPostProcessor.postProcessBeforeInitialization()
- 调用@PostConstruct / InitializingBean.afterPropertiesSet() / init-method
- 调用BeanPostProcessor.postProcessAfterInitialization()
4. 使用(Usage)
5. 销毁(Destruction)
- 调用@PreDestroy / DisposableBean.destroy() / destroy-method
2.2 业务场景:电商订单服务初始化
@Component
public class OrderService implements InitializingBean {
@PostConstruct
public void init() {
// 初始化订单号生成器
// 加载订单规则配置
}
@PreDestroy
public void destroy() {
// 关闭资源连接
}
}
三、线程池核心参数详解
3.1 七大核心参数
| 参数 | 说明 | 建议值 | |------|------|--------| | corePoolSize | 核心线程数 | CPU核心数 | | maximumPoolSize | 最大线程数 | CPU核心数*2 | | keepAliveTime | 空闲线程存活时间 | 60s | | unit | 时间单位 | TimeUnit.SECONDS | | workQueue | 工作队列 | LinkedBlockingQueue | | threadFactory | 线程工厂 | 自定义命名 | | handler | 拒绝策略 | 根据业务选择 |
3.2 四种拒绝策略
| 策略 | 行为 | |------|------| | AbortPolicy | 抛出RejectedExecutionException | | CallerRunsPolicy | 调用者线程执行任务 | | DiscardPolicy | 直接丢弃任务 | | DiscardOldestPolicy | 丢弃队列最老任务 |
3.3 业务场景:秒杀异步处理
ThreadPoolExecutor seckillExecutor = new ThreadPoolExecutor(
10, // 核心线程:处理常规秒杀
50, // 最大线程:应对突发流量
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 缓冲1000个请求
new ThreadFactoryBuilder().setNameFormat("seckill-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 降级到调用者执行
);
四、Redis缓存一致性方案详解
4.1 延迟双删策略
时间线:
T0: 删除缓存
T1: 更新数据库
T2: 延迟500ms-1s
T3: 再次删除缓存
为什么需要延迟?
- 在T1和T3之间,可能有其他线程读取旧数据并写入缓存
- 延迟时间 > 业务读写时间,确保旧数据不会重新写入缓存
4.2 消息队列异步更新
1. 更新数据库
2. 发送MQ消息(包含key)
3. 消费者异步删除缓存
4. 失败重试机制
4.3 业务场景:电商库存扣减
// 延迟双删实现
public void updateStock(Long skuId, int quantity) {
// 1. 删除缓存
redisTemplate.delete("stock:" + skuId);
// 2. 更新数据库
stockMapper.updateStock(skuId, quantity);
// 3. 延迟删除
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(500);
redisTemplate.delete("stock:" + skuId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
五、Redis分布式锁详解
5.1 基础实现
// 加锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent("lock:order:" + orderId, "1", 30, TimeUnit.SECONDS);
// 解锁(Lua脚本保证原子性)
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("lock:order:" + orderId), "1");
5.2 Redisson看门狗机制
RLock lock = redissonClient.getLock("lock:order:" + orderId);
try {
// 加锁,默认30秒过期,自动续期
lock.lock();
// 业务逻辑
} finally {
lock.unlock();
}
WatchDog原理:
- 默认锁过期时间30秒
- 每10秒检查一次,如果线程还持有锁,自动续期到30秒
- 线程结束或异常时停止续期
六、Dubbo服务调用流程详解
6.1 完整调用链路
1. Consumer发起调用
2. 从Registry获取Provider列表
3. 负载均衡选择Provider
4. 序列化请求参数
5. 网络传输(Netty)
6. Provider反序列化
7. 执行业务逻辑
8. 序列化响应结果
9. 网络传输返回
10. Consumer反序列化结果
6.2 负载均衡策略
| 策略 | 说明 | 适用场景 | |------|------|----------| | RandomLoadBalance | 加权随机 | 默认,服务器性能不均 | | RoundRobinLoadBalance | 加权轮询 | 服务器性能相近 | | LeastActiveLoadBalance | 最少活跃调用 | 处理慢的机器少分配 | | ConsistentHashLoadBalance | 一致性哈希 | 有状态服务 |
七、Kafka消息积压处理方案
7.1 紧急扩容方案
1. 临时创建新Topic(更多Partition)
2. 原Consumer转发消息到新Topic
3. 新Consumer消费新Topic
4. 消费完成后恢复原架构
7.2 优化方案
| 方案 | 说明 | |------|------| | 增加Partition | 同步增加Consumer数量 | | 批量消费 | 提高单次消费消息数 | | 异步处理 | 消费后异步处理业务 | | 多线程消费 | Consumer内部多线程处理 |
八、分布式事务方案对比
8.1 TCC模式
Try阶段:资源预留(如冻结库存)
Confirm阶段:真正执行(扣减库存)
Cancel阶段:释放资源(解冻库存)
优点:性能好,无锁等待 缺点:代码侵入性强,需实现三个接口
8.2 基于消息队列的最终一致性
1. 本地事务执行
2. 发送消息到MQ
3. 下游服务消费消息
4. 消费失败重试
5. 死信队列人工处理
优点:解耦,异步 缺点:非强一致,需要幂等设计
📝 面试总结
| 考察维度 | 掌握程度 | 建议 | |----------|----------|------| | Java基础 | ⭐⭐⭐⭐ | 深入源码理解 | | Spring框架 | ⭐⭐⭐⭐ | 掌握生命周期 | | 并发编程 | ⭐⭐⭐ | 多实践线程池 | | Redis | ⭐⭐⭐ | 理解一致性方案 | | 微服务 | ⭐⭐⭐ | 了解调用链路 | | 消息队列 | ⭐⭐ | 学习积压处理 | | 分布式事务 | ⭐⭐ | 理解各方案优劣 |
💡 写在最后:面试不是背八股文,而是要理解技术背后的原理和业务场景。谢飞机虽然回答得磕磕绊绊,但他至少敢于表达。希望各位求职者都能充分准备,在面试中展现最好的自己!
如果觉得这篇文章对你有帮助,别忘了点赞、收藏、关注哦! 👍
#Java面试 #SpringBoot #微服务 #Redis #分布式系统
更多推荐

所有评论(0)