Java大厂面试实录:从Spring Boot到微服务架构,谢飞机的三轮闯关记

面试官:王总(某互联网大厂技术总监,严肃认真) 求职者:谢飞机(水货程序员,自信满满但技术半吊子)


【第一轮面试:Java基础与Spring Boot】

场景设定:面试开始,王总翻开简历,抬头看了一眼紧张兮兮的谢飞机。

王总:(面无表情)谢飞机是吧?看你简历上写做过电商项目,那我们就从电商场景聊起。先问几个基础问题。

王总:第一个问题,你在电商项目里肯定用过HashMap来缓存商品信息,那你说说HashMap的底层实现原理?

谢飞机:(眼睛一亮,这题我背过!)王总好!HashMap底层是数组+链表+红黑树实现的。当发生哈希冲突时,链表长度超过8且数组长度超过64就会转成红黑树,这样查询时间复杂度从O(n)降到O(log n)。put的时候先算hash值,通过(n-1)&hash定位数组位置...

王总:(微微点头)嗯,基础还不错。那Spring Boot的自动配置你了解吗?我们项目大量使用Spring Boot,你来说说@SpringBootApplication注解背后做了什么?

谢飞机:(继续自信)这个我也知道!@SpringBootApplication是个组合注解,包含@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan。核心是@EnableAutoConfiguration,它会通过SpringFactoriesLoader加载META-INF/spring.factories文件中的配置类,根据@Conditional注解判断是否生效...

王总:(露出赞许的微笑)不错,看来基础功课做得可以。最后一个问题,电商秒杀场景下并发量很大,你用过线程池吗?说说线程池的核心参数有哪些?

谢飞机:(拍胸脯)线程池我太熟了!7个核心参数:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • unit:时间单位
  • workQueue:工作队列
  • threadFactory:线程工厂
  • handler:拒绝策略

当任务来时,先判断核心线程是否满,满了就放队列,队列满了就创建非核心线程,都满了就执行拒绝策略!

王总:(满意地点头)很好,基础很扎实。那我们进入下一个环节...


【第二轮面试:微服务与中间件】

场景设定:王总喝了口茶,表情开始变得严肃。

王总:基础不错,现在我们聊聊实际业务场景。我们有个电商秒杀系统,高峰期QPS能达到10万+。第一个问题,如何用Redis保证秒杀时的库存一致性?

谢飞机:(开始有点虚)呃...这个...用Redis的话...就是先把库存预加载到Redis里,然后用...用那个...decr操作?对,用decr原子操作来扣减库存,这样可以保证...保证不超卖...

王总:(皱眉)只是decr吗?如果Redis和数据库数据不一致怎么办?

谢飞机:(擦汗)那就...那就用消息队列异步同步?或者...用那个分布式锁?

王总:(叹气)好吧,那你详细说说分布式锁怎么实现?

谢飞机:(更加紧张)分布式锁...用Redis的话就是用setnx...设置一个key...然后设置过期时间...防止死锁...具体实现类是...是Redisson?

王总:(摇头)说得不够清晰。那我们换个话题,你们项目用Dubbo做服务治理吗?说说Dubbo的服务调用流程?

谢飞机:(含糊其辞)Dubbo的话...消费者调用...然后通过注册中心...找到提供者...然后RPC调用...具体流程是...是那个...序列化、网络传输、反序列化...

王总:(明显不满意)太笼统了。最后一个问题,我们系统用Kafka做消息队列,如果出现消息积压怎么处理?

谢飞机:(开始胡扯)消息积压...那就增加消费者?或者...优化消费逻辑?或者...扩容?具体方案我...我项目中没遇到过这种情况...

王总:(面无表情记录)好的,我们进入最后一轮...


【第三轮面试:分布式架构与系统设计】

场景设定:王总合上简历,眼神犀利。

王总:最后一轮,我们聊聊系统设计。假设让你设计一个支持百万并发的电商秒杀系统,你会怎么设计整体架构?

谢飞机:(彻底懵圈)百万并发...这个...首先...要用微服务架构...然后...用Redis缓存...再用...用消息队列削峰...具体怎么设计...我...我画不出来...

王总:那分布式事务怎么保证一致性?比如秒杀下单,要同时扣库存、创建订单、扣余额,怎么保证这三个操作要么全成功要么全失败?

谢飞机:(开始瞎编)分布式事务...用...用2PC?或者...那个...TCC?还是...Saga?具体实现...我...我用的是本地事务...

王总:(摇头)最后两个问题。第一个,Spring AI你了解吗?我们准备在电商系统中引入AI智能客服。

谢飞机:(完全不知道)Spring AI...是...是Spring框架的AI模块吗?我...我没用过...

王总:那MCP协议呢?Model Context Protocol?

谢飞机:(一脸茫然)MCP...是...是Microservice Communication Protocol吗?

王总:(叹气,合上笔记本)好了,今天的面试就到这里。你的基础还可以,但在分布式架构和新技术方面还有欠缺。我们会综合评估,三天内给你答复,你先回去等通知吧。

谢飞机:(尴尬起身)好的好的,谢谢王总,那我等您通知...


【技术详解:让小白也能学会】

一、HashMap底层原理

业务场景:电商系统中缓存商品信息、用户会话等

技术要点

  1. 数据结构:JDK1.8采用数组+链表+红黑树
  2. 哈希计算(key.hashCode() ^ (key.hashCode() >>> 16)) & (n-1),高位参与运算减少冲突
  3. 树化条件:链表长度≥8 且 数组长度≥64
  4. 扩容机制:容量翻倍,重新hash分布
// 核心put流程
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

二、Spring Boot自动配置原理

业务场景:快速搭建电商微服务,减少配置工作

技术要点

  1. 核心注解@EnableAutoConfiguration
  2. 加载机制:通过SpringFactoriesLoader读取META-INF/spring.factories
  3. 条件装配@ConditionalOnClass@ConditionalOnMissingBean
  4. 配置覆盖:用户自定义Bean优先于自动配置
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

三、线程池核心参数

业务场景:电商秒杀、批量订单处理等高并发场景

技术要点

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10,                    // corePoolSize: 核心线程数
    20,                    // maximumPoolSize: 最大线程数
    60L,                   // keepAliveTime: 空闲线程存活时间
    TimeUnit.SECONDS,      // unit: 时间单位
    new LinkedBlockingQueue<>(1000),  // workQueue: 工作队列
    new ThreadFactory() {  // threadFactory: 线程工厂
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setName("seckill-thread-" + t.getId());
            return t;
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy()  // handler: 拒绝策略
);

拒绝策略

  • AbortPolicy:抛出异常(默认)
  • CallerRunsPolicy:调用者线程执行
  • DiscardPolicy:直接丢弃
  • DiscardOldestPolicy:丢弃最老任务

四、Redis分布式锁

业务场景:秒杀库存扣减、订单状态更新

技术要点

// Redisson实现分布式锁
RLock lock = redissonClient.getLock("seckill:lock:" + productId);
try {
    // 加锁,等待3秒,锁定10秒自动释放
    if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
        try {
            // 扣减库存
            Long stock = redisTemplate.opsForValue().decrement("seckill:stock:" + productId);
            if (stock < 0) {
                throw new RuntimeException("库存不足");
            }
            // 创建订单...
        } finally {
            lock.unlock();
        }
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

关键问题

  1. 原子性:使用Lua脚本保证判断和删除的原子性
  2. 可重入:Redisson通过hash结构支持可重入
  3. 看门狗机制:自动续期,防止业务未完成锁就释放

五、Kafka消息积压处理

业务场景:订单创建后异步通知库存、积分、物流等系统

解决方案

  1. 增加消费者:扩容Consumer实例数(不超过Partition数)
  2. 优化消费逻辑:批量处理、异步化
  3. 临时扩容:创建更多Partition,迁移数据
  4. 降级处理:记录到数据库,后续补偿
// 批量消费优化
@KafkaListener(topics = "order-create", groupId = "order-group")
public void consume(List<ConsumerRecord<String, String>> records) {
    // 批量处理
    List<OrderMessage> messages = records.stream()
        .map(record -> JSON.parseObject(record.value(), OrderMessage.class))
        .collect(Collectors.toList());
    
    // 批量入库
    orderService.batchProcess(messages);
}

六、Dubbo调用流程

业务场景:电商微服务间RPC调用

完整流程

1. 消费者启动,向注册中心订阅服务
2. 提供者启动,向注册中心注册服务
3. 注册中心返回提供者地址列表给消费者
4. 消费者调用时:
   - 从地址列表负载均衡选择一个提供者
   - 序列化请求参数
   - 通过Netty发送网络请求
   - 提供者接收请求,反序列化
   - 执行本地方法
   - 序列化结果返回
   - 消费者反序列化结果

七、分布式事务解决方案

业务场景:秒杀下单(扣库存+创建订单+扣余额)

方案对比: | 方案 | 一致性 | 性能 | 复杂度 | 适用场景 | |------|--------|------|--------|----------| | 2PC | 强一致 | 低 | 高 | 传统银行系统 | | TCC | 最终一致 | 中 | 高 | 资金类业务 | | Saga | 最终一致 | 高 | 中 | 长事务 | | 本地消息表 | 最终一致 | 高 | 低 | 电商订单 |

本地消息表实现

@Transactional
public void createOrder(OrderRequest request) {
    // 1. 创建订单
    orderMapper.insert(order);
    
    // 2. 写入本地消息表
    messageMapper.insert(new Message(
        UUID.randomUUID().toString(),
        "ORDER_CREATED",
        JSON.toJSONString(order),
        MessageStatus.PENDING
    ));
    
    // 3. 扣减库存(调用库存服务)
    inventoryService.deduct(request.getProductId(), request.getQuantity());
}

// 定时任务发送消息
@Scheduled(fixedDelay = 1000)
public void sendPendingMessages() {
    List<Message> messages = messageMapper.selectPending();
    for (Message msg : messages) {
        try {
            kafkaTemplate.send("order-topic", msg.getContent());
            msg.setStatus(MessageStatus.SENT);
            messageMapper.update(msg);
        } catch (Exception e) {
            // 重试或告警
        }
    }
}

八、Spring AI与MCP协议

Spring AI:Spring官方的AI集成框架,提供统一的API对接各大模型

@Service
public class AIService {
    @Autowired
    private ChatClient chatClient;
    
    public String chat(String message) {
        return chatClient.prompt()
            .user(message)
            .call()
            .content();
    }
}

MCP(Model Context Protocol):Anthropic提出的模型上下文协议,标准化AI与外部工具的交互

核心概念

  • Resources:数据源(文件、数据库等)
  • Tools:可执行的操作
  • Prompts:提示词模板

面试总结

谢飞机虽然基础尚可,但在以下方面需要加强:

  1. 分布式系统实战经验:需要深入理解分布式锁、事务、一致性等
  2. 系统设计能力:需要培养从0到1设计高并发系统的能力
  3. 新技术敏感度:需要关注Spring AI、MCP等前沿技术

给求职者的建议

  • 基础要扎实,但更要注重实战
  • 多参与高并发项目,积累经验
  • 持续关注技术社区,保持学习热情

本文通过幽默的面试场景,覆盖了Java面试的核心知识点。希望对正在准备面试的同学有所帮助!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐