ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java面试实战:Spring Boot、微服务与SSE流式输出全攻略

Java面试实战:Spring Boot、微服务与SSE流式输出全攻略 最近帮几位朋友做了一轮大厂Java面试的模拟复盘发现一个明显变化面试官已经不满足于“背八股”了。Spring Boot、微服务依然是必考底盘但AI技术相关的追问越来越多经常一开口就是“你项目里大模型回答是怎么流式渲染的”“客户端点了停止生成你后端怎么处理”。这其实是一件好事说明市场想要的不是会背题的而是真在自己项目里把Spring Boot、微服务、AI集成打通过的人。这篇文章是我把这几个月准备面试和帮人复盘时的高频考点结合具体实操经验整理成的一份实战笔记。内容覆盖Spring Boot启动原理与Bean注入、微服务拆分与分布式事务、基于SSE流式输出封装大模型交互逻辑最后附一份面试现场的高频问题与排查速查表。无论你是在准备跳槽还是做业务系统想往AI方向靠都可以直接拿来当复习主线。1. Spring Boot从第一个程序到Bean生命周期面试官到底在考什么1.1 “第一个Spring Boot程序”不只是Hello World而是启动原理很多人在准备面试时觉得“第一个Spring Boot程序”太简单随手就能跑起来。但恰恰是这个问题能最快速地区分“用过”和“懂原理”两类候选人。面试官不会只问“你怎么创建项目”而是会顺着往下追问为什么一个main方法就能把Web服务拉起来内嵌Tomcat是谁启动的自动配置到底是怎么发生的这些问题的根都在SpringBootApplication这个复合注解上。它等价于SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个注解的组合。ComponentScan负责扫描当前包及子包下的BeanEnableAutoConfiguration通过AutoConfigurationImportSelector加载spring.factories或者AutoConfiguration.imports文件里声明的配置类按条件注解ConditionalOnClass、ConditionalOnMissingBean决定是否生效内嵌Tomcat就是ServletWebServerFactoryAutoConfiguration这个自动配置类在工作它创建TomcatServletWebServerFactory再启动Servlet容器。整个链路其实和以前Spring手动拼XML配置没有本质区别只不过把“程序员决定建什么Bean”变成了“框架按条件猜测你需要什么Bean”。所以我建议每个准备面试的人一定要把“第一个Spring Boot程序”完整地在源码层面走一遍。最直观的验证方式是打开META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件看看列表再打断点看AutoConfigurationImportSelector的getCandidateConfigurations方法。一旦你看过这些类面试官再问“starter的工作原理是什么”“为什么引入spring-boot-starter-web就有Web环境”你都能答到点子上。1.2 Bean注入的“控制权”与循环依赖别只会用AutowiredSpring Boot核心考点里Bean的注入方式几乎必考。很多人的第一反应是Autowired但面试官真正想听的是你对“控制反转”的理解有多深。注入方式本质上有三种字段注入、Setter注入、构造器注入。字段注入代码写起来最爽尤其在Controller里一行Autowired private UserService userService;就完事但它的问题也很明显依赖隐藏、不可final、单元测试不友好、类无法真正脱离容器运行。构造器注入能明确表达“这个Bean没有这些依赖就创建不了”测试时可以手动new并传入mock依赖所以Spring官方也推荐构造器注入。比注入方式更常见的是循环依赖问题。A依赖B、B依赖A在Spring里可以通过三级缓存解决一级缓存放完整Bean二级缓存放提前暴露的早期Bean三级缓存放ObjectFactory。但如果你在项目里遇到过Spring Boot 2.6以后的报错会发现默认已经禁止循环依赖了spring.main.allow-circular-references这个配置默认关闭。Spring Boot 3和Spring Framework 6上缓存策略也在收紧网上那套“三级缓存包治百病”的旧知识点需要更新。这里有一个很实的建议面试时不要上来就背三级缓存而是先解释“Spring为什么不能直接解决构造器循环依赖”——因为构造器注入要求Bean实例化时就必须保证依赖齐全A要创建B、B又要创建A谁都没法先实例化这是“鸡生蛋”问题。能说出这一层面试官会立刻觉得你不只是背了面经。1.3 Spring Boot 3的Security迁移、WebSocket与日志配置Spring Boot 3和Spring Security 6出来后网上很多老教程已经不能直接用了这也成了一个高频率面试点。最典型的是原先继承WebSecurityConfigurerAdapter的写法被彻底移除现在的标准写法是直接定义SecurityFilterChainBean。配置上从http.authorizeRequests()迁移到http.authorizeHttpRequests()方法链从and()改成Lambda DSL写法。如果你在项目里迁移过大概率会遇到权限规则不生效或默认登录页还在的情况根因往往是忘了csrf和session策略也要跟着配或者静态资源路径没有放行。WebSocket也是Spring Boot面试的高频延伸点。面试官常问的不只是“怎么加一个WebSocket端点”而是“握手拦截器怎么配置、token在哪里校验、STOMP消息代理怎么连RabbitMQ”。如果项目里只用原生WebSocketyml里配置其实很简单设置端点路径、设置允许跨域来源、配置SockJS兜底。真正容易踩坑的地方是WebSocket握手请求不会经过普通Spring MVC拦截器所以JWT校验必须在HandshakeInterceptor里手动做另外用了STOMP后心跳和断线重连是最容易被忽略的线上经常是“看起来连着但消息发不出去”。日志配置在面试中虽然占比不大但做项目时很实。Spring Boot默认用Logback你需要知道logging.level、logging.file.name这些基础配置也要知道自定义logback-spring.xml时用springProfile标签做多环境格式区分。我建议把日志体系单独整理成一篇笔记因为线上排查大模型接口超时、SSE断连时日志打得好不好直接决定你定位问题的速度。1.4 本地缓存Caffeine与缓存设计Spring Boot里的“加速器”热词里出现了spring boot caffeine这也是我近期在面试中被问到较多的小题。Caffeine是高性能本地缓存库Spring Boot 2.x以后官方推荐用它替代Guava Cache。配置上就一行spring.cache.typecaffeine再配一个CaffeineCacheManager就能用。但面试问到缓存时别只答“用Caffeine做本地缓存”。更合理的思路是讨论多级缓存本地Caffeine存热点数据Redis存分布式共享缓存DB兜底。为什么不能全用Redis因为每次查询都走网络IO性能不如本地内存快但本地缓存又存在多实例数据不一致问题。所以常规做法是一级Caffeine的过期时间设短一点比如30秒二级Redis的过期时间设长一点比如5分钟再通过MQ广播清理本地缓存。这套方案在商品列表、白名单配置、模型回复缓存这类场景非常实用。2. 微服务架构拆分逻辑、分布式事务与框架选型2.1 Spring、Spring Boot、Spring Cloud到底有什么区别微服务面试有一个绕不开的基础问题Spring、Spring Boot、Spring Cloud到底是什么关系。很多初级开发容易混为一谈。我的讲法是用一个类比Spring是“地基和建材”提供IoC容器、AOP、事务管理等基础设施Spring Boot是“精装修”通过自动配置和starter直接给你一套能跑起来的房子Spring Cloud则是“小区管理系统”负责多栋房子之间的通信、安全、路由和监控。没有Boot搭建Cloud全家桶会非常痛苦没有SpringBoot的底层能力无从谈起。如果面试官继续追问“微服务架构里Spring Cloud的核心组件有哪些”你需要能列举注册中心Nacos/Eureka、配置中心Nacos Config/Spring Cloud Config、网关Gateway、负载均衡LoadBalancer、熔断和限流Sentinel/Resilience4j、OpenFeign声明式HTTP调用、链路追踪Micrometer Tracing/SkyWalking。注意旧版网课里常用的Zuul和Hystrix在Spring Cloud 2021以后基本处于维护状态面试中可以说“了解但不用在项目里”避免暴露技术栈陈旧。2.2 微服务拆分实战商城系统的边界到底怎么划面试里最考验真实水平的场景题就是“给你一个单体商城系统你怎么拆微服务”。这个题没有标准答案但可以约成三步走先找业务边界再定接口契约最后做数据隔离。第一步按业务域划分。典型的商城可以拆成用户、商品、库存、订单、支付、购物车、营销、评价等几个中心。划分时有一个判断标准很实用两个功能模块如果频繁跨库联查就说明边界划错了如果某个模块的改动经常牵扯另一个模块一起上线说明它们应该合并。第二步定接口契约。每个服务对外暴露API时要有明确的入参出参、限流阈值和版本号服务之间不允许直接访问对方的数据库表。第三步数据隔离是拆分中最痛的一步订单服务不能随便查用户表的全部字段只能通过用户服务提供的“用户摘要”接口拿数据这就是服务自治。拆分时还要警惕“分布式单体”陷阱。很多团队把服务拆出来了但一次业务操作还是要同步调用七八个服务最后整出一个“慢速单体”。正确做法是先画清楚“一次下单需要几次服务间调用”能合并的服务间调用就通过聚合接口合并能异步的就上MQ不要迷信服务数量越多越微服务化。2.3 数据一致性分布式事务的“常规解法”和面试话术微服务面试里如果只能押一个深度题我会押“Java怎么保证数据一致性”。因为单体数据库事务好聊但拆了服务后一个订单创建要同时扣库存、加积分、发优惠券这些分散在不同服务的本地事务里怎么保证整体要么全成功要么全失败市面上有几种主流方案面试时必须清楚各自的代价。最常用于业务系统的是最终一致性本地事务 消息表 MQ。下单时先把订单数据和待发送消息写到同一个本地数据库事务里事务成功后再异步投递MQ下游订阅消息执行扣库存、加积分。如果MQ投递失败定时任务扫消息表重投。这个方案思路简单、可用性强但存在消息重复问题所以下游消费逻辑必须“幂等”。更严格的场景用TCCTry、Confirm、Cancel或Saga。TCC需要对每个参与方实现三段接口开发和协调成本很高Saga适合长流程通过编排服务依次调各个子任务失败时反向补偿。在面试话术上我建议这样组织先反问业务是不是真的需要强一致性再给出“默认最终一致性、特殊账户场景才用TCC或Saga”的原则最后强调“幂等是分布式一致性的基石接口必须能对同一个请求重复执行不产生副作用”。答出这套逻辑面试官会认为你有真实架构判断力而不是只会背方案名。2.4 开源框架与通信协议若依微服务Plus、gRPC与服务联调最近很多人在问若依微服务Plus能不能写进简历。它是一个基于Spring Cloud的快速开发平台架构上用了Nacos做注册和配置中心、GateWay做网关、Sentinel做流控、Seata做分布式事务、Redis和Mysql存储。如果你是在小公司做完项目没有太多架构设计机会拿若依这类开源框架作为“二次开发学习样板”完全没问题但简历上不要写“架构由我设计”要写“基于若依微服务Plus搭建了XX管理系统实现了动态权限和XX业务模块”重点突出业务交付落地的能力。服务间通信协议也是面试常客。Feign/OpenFeign是同步HTTP调用开发心智成本低接口直接按Controller风格写但大流量、对性能敏感的场景更适合gRPC它基于HTTP/2和Protobuf二进制序列化建立连接后长连接复用性能比JSON HTTP/1.1好不少。Spring Boot集成gRPC时需要注意版本兼容性spring-boot-starter和grpc-spring-boot-starter的版本要匹配否则会碰到服务注册不上、MethodDescriptor找不到之类的问题。还有一个很实际的细节就是微服务和系统怎么启动与联调。本地排查问题时不要一次把七八个服务全拉起来先起Nacos、Mysql、Redis这些基础设施再按依赖顺序起基础服务、中间层服务、网关服务。联调时建议给每个服务配置独立的bootstrap.yml用--spring.profiles.activedev区分环境并且开Spring Cloud OpenFeign的日志级别为DEBUG能省下非常多排查时间。3. AI技术栈大模型交互逻辑封装与SSE流式输出3.1 基于什么技术栈封装AI交互逻辑这两年Java后端接到最多的新需求就是把大模型能力集成进业务系统。面试里高频出现的措辞是“基于什么技术栈封装AI交互逻辑”这其实是在考察你有没有完整做过“后端接模型、模型流式返回、前端实时渲染”这条链路。我的建议是从简到繁分两层。简单方案是直接在Spring Boot项目里引入HTTP客户端OkHttp、WebClient或RestTemplate调用模型服务商提供的对话补全接口把整段回复拿回来后一次性返回给前端。这个方案适合内部工具、一次性问答但用户体验差大模型生成可能要几十秒前端按钮一直转圈用户流失严重。进阶方案是封装一个独立的LLMProxy模块统一管理API密钥、模型名称、温度参数、上下文组装、限流和审计日志。Spring生态里可以直接用Spring AI框架它提供统一的ChatClient接口底层可以切换OpenAI、Ollama、Azure OpenAI等还能用MessageMapping做流式输出。这块技术栈选型的核心逻辑是把“与模型服务商的交互细节”隔离在业务代码之外业务侧只关心“给我一个Prompt、返回一串流”这样模型厂商换了、接口协议变了业务代码不用动。3.2 SSE流式输出原理与实操从HTTP到EventSource大模型回答实时渲染业界标准方案是SSEServer-Sent Events。SSE本质上还是HTTP只是服务端响应头设置Content-Type: text/event-stream连接保持不关闭服务端可以多次推送data:开头的文本块。前端通过EventSource对象接收实现“打字机”效果。有些人会问既然WebSocket也能做流式输出为什么大模型场景选SSE。我的答案是WebSocket是双向全双工协议适合聊天室、实时协作这类“两端都要频繁发消息”的场景而大模型场景里用户提问是一次发完的之后就是服务端单方向持续吐数据用SSE更简单还能自动重连、有标准协议栈支持不需要额外维护WebSocket连接状态。SSE的缺点是浏览器原生的EventSource只支持GET如果你想带Authorization请求头得改用fetchReadableStream自己解析流。Spring Boot实现SSE最简单的方式是返回SseEmitter。流程是这样的Controller创建SseEmitter把实例交给一个异步线程在线程里调用大模型接口拿到流式响应每收到一段内容就通过emitter.send()推给前端全部完成后emitter.complete()。不要直接在主请求线程里同步阻塞调用模型因为这会占满Tomcat线程高并发下接口很快雪崩。更好的做法是用Async或者注入业务线程池处理模型调用。核心代码如下RestController RequestMapping(/api/ai) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } GetMapping(value /stream, produces text/event-stream) public SseEmitter stream(RequestParam String prompt) { // 0L表示不超时Saas项目里也可以设置为60秒或120秒 SseEmitter emitter new SseEmitter(0L); // 注册回调客户端断开或超时时释放资源 emitter.onCompletion(() - chatService.cancelUpstream()); emitter.onTimeout(() - { chatService.cancelUpstream(); emitter.complete(); }); emitter.onError(e - chatService.cancelUpstream()); chatService.streamChat(prompt, emitter); return emitter; } }对应服务层里调用大模型API时用WebClient的非阻塞方式流式接收并逐行解析data:内容并推送。要特别注意SSE中间不能经过会缓冲响应的Nginx或网关必须关闭缓冲X-Accel-Buffering: no否则前端看到的内容会一次性拥过来根本起不到“流式渲染”的效果。3.3 abort机制客户端断连时如何及时取消大模型请求热词里那句“配合abort”我觉得是SSE面试里最容易被追问的点。场景是用户在界面上点了“停止生成”前端应该取消渲染后端也必须同步中断大模型请求否则模型还会继续生成答案Token照扣这就是浪费成本。前端侧用AbortController可以做到。把signal传给fetch点击停止时调用controller.abort()浏览器会终止当前请求连接被关闭。这里有个细节很多开发者只在前端停了渲染没有通知后端后端的SseEmitter可能还在傻等模型返回或者模型返回后往一个已经断开的连接里写数据。所以后端要注册断连监听SseEmitter.onCompletion并不是只在正常结束时回调客户端断开也会触发你要在回调里调用模型SDK的cancel方法断开与模型服务的连接。如果是自定义HTTP客户端可以保存当前请求的Future或Disposable然后在回调里cancel()。实现abort还有一个常见坑如果用的是Spring WebClient流的取消需要拿到Disposable句柄如果用的是OkHttp3取消时调用Call.cancel()如果用的是OpenAI SDK底层请求对象往往提供了cancel()方法。我项目中会专门做一个StreamTaskManager组件用ConcurrentHashMaprequestId, Disposable记录所有在途流式请求停止生成时通过requestId精准取消同时记录日志观察取消链路耗时。面试官听到这个地方一般会很感兴趣因为这说明你真的为“Token计费”考虑过。3.4 线上化避坑超时、并发、Token消耗把AI交互逻辑封装好后线上运行还会踩到几个实际问题。第一是超时问题。默认HTTP客户端的连接超时一般只有几秒但大模型首字返回可能要等10秒以上所以必须区分“连接超时”和“读超时”读超时建议拉到60秒首字超时单独判断。如果请求被负载均衡到多个后端实例Nginx的proxy_read_timeout也要同步调大否则你后端还没推完第一块数据网关就给你掐断连接了。第二是并发控制。大模型接口是慢接口线程池很容易被打满所以需要信号量或者限流器控制总并发数。我习惯用Resilience4j的Bulkhead做隔离给AI接口单独开一个线程池配置最大并发和队列容量。一定不要把AI请求放进Tomcat默认线程池否则一个高并发AI功能就能把整个系统拖垮。第三是Token成本控制接入层要按用户维度做频控并在响应里返回“本次消耗Token数”方便运营核算成本。4. 高频面试题速查与现场复盘4.1 Java基础与并发面向对象、容器、AQS大厂面试的“第一面”往往还是Java基础但问法越来越场景化。比如“面向对象三大特性”不会直接让你背定义而是让你对比“封装和抽象的区别”“继承和组合怎么选”。Java容器的重点还是HashMap强烈建议把JDK 1.7多线程扩容成环、1.8引入红黑树条件链表长度 8 且数组长度 64、为什么阈值为0.75往死里背熟。并发部分AQS是一个高权重考点。AbstractQueuedSynchronizer几乎撑起了ReentrantLock、Semaphore、CountDownLatch这些同步工具。你至少要能讲清楚三件事内部用volatile int state记录锁状态获取锁失败的线程包装成Node节点进入CLH双向队列排队acquire方法里做“尝试获取 入队 阻塞”的循环。如果再深入一点可以提到公平锁和非公平锁在tryAcquire上的差异非公平锁会在入队前先抢一次锁所以吞吐更高但可能造成线程饥饿。4.2 微服务与AI场景的追问套路到了第二轮项目面试面试官通常不背题而是围绕你的项目简历连环追问。微服务方向常问“你这个服务拆分的依据是什么”“如果订单服务挂了怎么保证用户还能浏览商品”“分布式锁用Redis还是Zookeeper为什么”。AI方向则常问“你的Prompt是写死在代码里还是可配置”“大模型返回的JSON格式不稳定你怎么处理”“多轮对话的上下文怎么存”。我自己的经验是AI场景题不要把话说太满加一句“我们当前的实现是……但考虑到……后续会调整为……”反而更可信。比如“上下文管理”这个问题可以如实回答“先存Redis设置30分钟过期每次把最近10条历史组装后发给模型”然后补一句“现在用户量上来后我正在考虑用向量数据库做长期记忆”。这种“已经做过还在演进”的表达面试官非常吃这一套。4.3 常见问题排查表与避坑经验我把自己在项目中踩过、也帮人排查过的高频问题整理成一张表面试前过一遍很有用。现象可能原因排查思路Bean注入报NoSuchBeanDefinitionException服务类没加注解、包扫描路径没覆盖看启动类包路径确认ComponentScan范围MyBatis的Mapper要加MapperScanSpring Security登录后仍是403老写法迁移后权限规则没放开或静态资源被拦截检查SecurityFilterChain里的permitAll路径用Postman带token复现SSE接口等待很久才一次性返回响应在网关或Servlet容器被缓冲了Nginx关缓冲Spring Boot里设置producestext/event-stream确认已正常flushSseEmitter频繁报超时默认30秒超时大模型生成太久改为new SseEmitter(0L)或设置足够长的超时配好onTimeout回调前端abort后模型还在生成后端没有同步取消上游请求在emitter的onCompletion/onError里cancel WebClient的DisposableFeign调用失败但本地无日志日志级别未开启或服务未注册到Nacos开启logging.level包名DEBUG检查Nacos服务列表分布式事务里补偿逻辑不生效本地消息表重复投递导致幂等没做好先检查消费方是否按业务主键做幂等再查消息表状态流转最后再分享一个我个人的体会最近几次面试模拟中真正能拿到高评价的人不是把答案背得最全的人而是会主动讲“当时为什么这么做、踩了什么坑、后来怎么改”的人。比如SSE加abort机制一讲出“客户端断开后Token还要继续计费”这个真实痛点面试官的眼睛会亮。希望这份实战笔记能帮你在面试里把每个技术点讲得像“亲手解决过”的样子。
返回列表