互联网大厂高阶Java面试现场:从Spring AI到分布式事务的极限拷问
2026年3月24日杭州某互联网大厂会议室。空调恒温23℃白板笔迹未干面试官李工推了推眼镜目光平静却带着审视。对面坐着坤哥30岁出头穿着一件略显宽松的格子衬衫眼神里透着一股“我准备很久了”的笃定。“坤哥先简单自我介绍一下吧。”李工语气平和手指轻轻敲了敲桌面。“好的李工。我叫坤五年Java后端经验目前在一家中厂负责订单系统架构。熟悉Spring生态、分布式事务、高并发设计最近也在研究AI工程化落地比如用Spring AI做知识库助手。”李工点点头翻开笔记本“那我们从基础开始。你说熟悉SpringIOC和AOP是核心能说说Spring如何解决循环依赖”坤哥稍作思考“Spring通过三级缓存解决循环依赖。一级缓存是singletonObjects存放完整Bean二级缓存earlySingletonObjects存放早期暴露的Bean三级缓存singletonFactories存放ObjectFactory。当A依赖BB又依赖A时Spring在创建A时就将A的ObjectFactory放入三级缓存B在注入A时从三级缓存获取早期引用从而打破循环。”“那如果A和B都是原型作用域呢”李工追问。“原型作用域不支持循环依赖Spring会直接抛BeanCurrentlyInCreationException。”坤哥回答得干脆。“为什么”“因为原型Bean每次getBean都会新建实例无法通过缓存复用早期对象所以Spring干脆禁止。”李工点头“那三级缓存的设计动机是什么为什么不用二级”坤哥顿了一下“三级缓存主要是为了处理AOP代理。如果Bean需要被代理早期暴露的应该是代理对象而不是原始对象。ObjectFactory可以在需要时动态创建代理避免提前生成代理导致逻辑混乱。”“举个例子”“比如A被Transactional注解如果提前生成代理可能在B注入A时A的代理还未完全初始化导致事务失效。ObjectFactory延迟了代理的创建时机。”李工在笔记本上记了一笔“好进入下一轮。你提到Spring AI你们项目里怎么用”“我们用Spring AI RAG架构做企业知识库助手。用户提问后先通过向量数据库检索相关文档片段再拼接成Context交给LLM生成回答。”“向量数据库选型”“用的是Milvus支持高维向量相似度检索延迟低适合实时问答。”“Prompt怎么设计有没有做调优”“我们做了分层Prompt系统层设定角色和输出格式用户层拼接检索结果还加了Few-shot示例。调优方面试过Temperature、Top-p参数也做了Context Window压缩避免Token超限。”“Context Window压缩具体怎么做的”“用TF-IDF筛选关键句或者用Sentence-BERT重排优先保留高相关性片段。”“如果用户问题很模糊检索结果质量差怎么办”坤哥想了想“我们会加兜底策略比如返回‘未找到相关信息请尝试更具体的关键词’或者引导用户选择预设问题。”李工点头“那如果检索结果很多但LLM生成超时怎么优化”“可以异步处理先返回‘正在分析中’再通过WebSocket推送结果。或者做缓存对高频问题预生成答案。”“缓存一致性怎么保证”“用Redis做本地缓存设置TTL同时监听文档变更事件通过MQ通知更新缓存。”李工突然话锋一转“说到MQ你们用Kafka还是RocketMQ”“RocketMQ。因为需要事务消息Kafka的事务支持相对复杂RocketMQ的Half Message机制更直观。”“事务消息如何保证可靠性”“Producer发送Half消息到Broker执行本地事务再发送Commit/Rollback。Broker收到Commit后才对Consumer可见。如果Producer宕机Broker会回查本地事务状态。”“回查机制怎么实现”“Producer要实现TransactionListener接口提供executeLocalTransaction和checkLocalTransaction方法。Broker定时扫描Half消息调用checkLocalTransaction确认状态。”“如果本地事务长时间未提交会怎样”“Broker会持续回查默认最多回查15次间隔指数退避。如果最终未确认消息会被丢弃或进入死信队列。”“那如果Consumer消费失败怎么处理”“RocketMQ支持重试队列消费失败后进入%RETRY%队列延迟重试。超过重试次数进入死信队列人工介入。”李工喝了口水“好进入第三轮。假设你们知识库系统要支撑千万级用户日均问答量过亿怎么设计架构”坤哥深吸一口气“首先前端做限流用Sentinel做QPS控制。接入层用Nginx做负载均衡后端服务无状态横向扩展。”“服务层怎么拆分”“拆成三个微服务检索服务、生成服务、用户服务。检索服务负责向量检索生成服务调用LLM用户服务管理权限和会话。”“检索服务压力大怎么优化”“做多级缓存本地缓存 存热点问题Redis集群存检索结果Milvus做持久化向量库。同时做查询合并避免重复检索。”“生成服务调用LLM延迟高怎么应对”“用异步流式响应。用户提问后先返回‘正在思考’生成服务流式返回Token前端逐字渲染。同时做模型量化用vLLM加速推理。”“如果LLM服务宕机怎么降级”“降级到规则引擎比如关键词匹配FAQ或者返回预设模板‘服务繁忙请稍后再试’。”“数据一致性呢比如文档更新了向量库没同步”“用CDC监听MySQL binlog通过Debezium捕获变更发MQ通知向量库更新。同时做版本控制每次更新生成新版本向量旧查询仍用旧版本保证一致性。”“如果MQ积压向量库更新延迟怎么办”“监控积压量动态扩容消费者。或者做批量更新牺牲一点实时性换吞吐量。”李工在白板上画了个架构图“那如果用户量突然暴增比如某热点事件引发集中提问怎么扛住”“首先Sentinel做集群流控避免雪崩。其次检索服务做读写分离Milvus支持多副本。生成服务做模型预热提前加载权重。最后做请求排队超时就返回‘系统繁忙’。”“那如果Redis挂了缓存穿透怎么办”“用布隆过滤器过滤无效查询同时做空值缓存避免重复查库。还有限流避免DB被打崩。”“布隆过滤器误判率怎么控制”“根据预期元素数量和容忍误判率计算位数组大小和哈希函数数量。我们设的是0.1%误判率用Guava的实现。”李工点头“最后一个问题。如果你们系统要跨地域部署比如北京和广州两个机房怎么保证数据一致”坤哥沉默了几秒“这……可能需要分布式事务。比如用Seata的AT模式或者TCC。但跨机房延迟高AT模式锁冲突严重可能影响性能。”“那你怎么权衡”“如果强一致不是必须可以用最终一致。比如文档更新后先写本地库再发MQ通知对端机房异步同步。牺牲一点实时性换高可用。”“如果对端机房MQ挂了数据丢了呢”“加本地消息表记录同步状态定时重试。或者用可靠消息服务比如阿里云的MNS。”李工合上笔记本“好今天就到这里。坤哥你整体思路清晰但对跨机房一致性的方案还不够深入尤其是网络分区下的脑裂问题没提到。”坤哥点头“确实这块我经验不足回去会补。”李工站起身“回去等通知吧。”坤哥走出会议室阳光刺眼。他摸了摸口袋里的手机心想这次应该稳了吧技术补丁包1. Spring循环依赖与三级缓存原理Spring通过三级缓存解决单例Bean的循环依赖。一级缓存存完整Bean二级存早期暴露对象三级存ObjectFactory。设计动机三级缓存的核心是延迟代理对象的创建。若Bean需要AOP代理ObjectFactory可在注入时动态生成代理避免提前代理导致事务或切面失效。边界条件仅支持单例作用域原型Bean因无法复用早期对象而禁止循环依赖。源码关键DefaultSingletonBeanRegistry.getSingleton()方法中addSingletonFactory将ObjectFactory放入三级缓存。风险提示过度依赖循环依赖可能暗示设计缺陷建议通过重构消除。2. Spring AI与RAG架构核心流程用户提问 → 向量检索 → Context拼接 → LLM生成 → 返回答案。Prompt调优分层设计系统指令用户输入示例控制Temperature0.7~1.0避免过于随机使用Few-shot提升准确性。Context Window压缩TF-IDF筛选关键词句或Sentence-BERT重排确保高相关性内容优先进入LLM。落地建议结合业务FAQ做缓存预热降低冷启动延迟监控Token使用量避免超限。3. RocketMQ事务消息流程发送Half消息 → 执行本地事务 → 发送Commit/Rollback → Broker确认 → Consumer可见。回查机制Broker定时扫描Half消息调用Producer的checkLocalTransaction确认状态最多15次间隔指数退避。可靠性保障本地事务需幂等避免重复执行建议记录事务日志便于排查。对比KafkaRocketMQ事务API更简洁适合金融、订单等强一致性场景。4. 多级缓存与一致性层级设计本地缓存Caffeine → Redis集群 → 持久化存储MySQL/Milvus。缓存穿透布隆过滤器过滤无效Key空值缓存避免重复查库。缓存雪崩随机TTL避免集中失效降级策略返回默认值。一致性维护CDC监听数据变更MQ通知缓存更新版本控制保证查询一致性。5. 高并发架构设计限流降级Sentinel做QPS控制集群流控防雪崩降级到规则引擎或模板响应。异步处理生成服务流式返回提升用户体验MQ解耦提升系统韧性。跨机房部署最终一致性优先本地消息表定时重试保障数据同步避免强一致带来的性能损耗。监控告警关键指标QPS、延迟、错误率实时监控自动扩容应对突发流量。6. 分布式事务权衡强一致方案Seata AT模式全局锁、TCC业务侵入高。最终一致方案本地消息表、可靠消息队列、Saga模式。选型建议非金融场景优先最终一致牺牲实时性换高可用跨机房部署慎用强一致避免网络分区引发脑裂。