
Java大厂面试Spring Boot、JVM、Redis缓存、Kafka消息、微服务安全全解析——谢飞机欢乐面试记互联网大厂的面试间里阳光透过落地窗洒在白板上面试官老稳端坐在对面面前是一台开着IDE的笔记本电脑。谢飞机穿着一件皱巴巴的格子衫背着双肩包局促地坐下膝盖上的笔记本还没掏出来。他脸上努力挤出一个自信的笑容但眼神里透着一丝“我是谁我在哪儿”的迷茫。面试官老稳面无表情推了推眼镜开始提问。第一轮基础技术摸底面试官老稳先从Java基础开始。你平时用Java哪个版本说说Java 8的Stream和Lambda顺便聊聊Java 11和17带来的新特性。谢飞机我在公司里主力是Java 8毕竟牛马厂老项目都还在8上。Stream和Lambda我贼熟比如list.stream().filter(x - x.getId() 10).map(User::getName).collect(Collectors.toList())Lambda就是匿名内部类的简写嘛(a, b) - a b。Java 11我试过有个新的HttpClient还支持varJava 17是LTS好像有...那个什么密封类Sealed Class具体忘了反正就是限制继承的。面试官老稳不错var是Java 10引入的Sealed Class是Java 17正式发布的。说得基本对。那Spring Boot的自动配置原理是什么为什么你只要引入一个spring-boot-starter-web我就能直接写Controller了谢飞机这个我知道因为启动类上有个SpringBootApplication里面包含了EnableAutoConfiguration。这个注解会通过SpringFactoriesLoader加载META-INF里的spring.factories或者AutoConfiguration.imports文件把一堆自动配置类注册到容器里。还有一些条件注解比如ConditionalOnClass、ConditionalOnMissingBean如果类路径下存在某个类就初始化对应的Bean。所以引入Web的starter后Tomcat和DispatcherServlet就自动配好了。面试官老稳哦那条件注解的作用是什么谢飞机就是...根据条件决定是否装配这个Bean吧。比如没有自定义的ObjectMapper才自动配一个。大致这样。面试官老稳嗯说得不差。那ORM框架呢MyBatis和JPA你平时怎么选HikariCP连接池为什么快谢飞机MyBatis灵活SQL自己写适合报表、复杂查询JPA、Hibernate封装程度高简单CRUD非常爽还有二级缓存。HikariCP快是因为它代码少字节码精简用了一个叫ConcurrentBag的并发集合性能甩Druid和C3P0几条街。还有它默认的配置参数就优化得很好比如minimumIdle、maximumPoolSize。面试官老稳可以基础部分算过关了。那我们先到这里接下来聊聊实际场景。第二轮电商场景拷打面试官老稳假设我们有一个订单查询接口每秒1万次QPS数据库连接池都快被打爆了你会怎么做谢飞机这题我会加Redis缓存把订单信息都扔到缓存里查询先走Redis缓存没命中再查数据库。面试官老稳那缓存穿透、缓存击穿、缓存雪崩分别是什么怎么解决谢飞机呃...穿透是查一个垃圾key缓存里没有数据库也没有导致每次都要打DB。击穿是某个热点key突然过期大量请求同时打到DB。雪崩是很多key一起过期或者Redis挂了。解决办法就是...穿透用布隆过滤器或者缓存空值击穿的话给热点key加分布式锁雪崩就把过期时间加个随机值或者搞多级缓存、Redis高可用。面试官老稳说得还算沾边。那支付成功后的消息你怎么保证不丢Kafka消费者如何处理消息谢飞机我们用的是Kafka生产者设置acksall然后开事务消费者处理完业务逻辑后手动提交offset这样能避免丢消息。消费端要保证幂等用Redis的setnx或者数据库唯一索引防止重复消息把订单状态改错。面试官老稳那如果Kafka broker挂了呢消息会不会丢谢飞机嗯broker挂的话……多副本应该能扛设置min.insync.replicas2还有一个ISR机制leader和follower同步。但要是只有一个副本那可能就没了。这块我平时主要是调参不是特别深。面试官老稳行。换个问题微服务架构里服务注册发现用Eureka还是Consul有什么区别OpenFeign调用超时怎么设置谢飞机我们公司用的Nacos……不过Eureka和Consul我知道。Eureka是AP模型服务注册后不保证实时一致但可用性高Consul是CP模型强一致但发生分区时可能不可用。OpenFeign超时配置是用的Ribbon设置ribbon.ConnectTimeout5000和ribbon.ReadTimeout10000还有feign.retryableExceptions吧对了如果要熔断降级用Resilience4j不用Hystrix了反正都是搞熔断限流。面试官老稳嗯Resilience4j是新的趋势你能提出来就不错。但你说的OpenFeign超时设置不完全准确Feign有自己的时间配置要区分连接超时和读取超时。这块我们后面再讨论。第三轮安全与运维的极限追问面试官老稳你的服务要做登录认证JWT和OAuth2有什么区别如果让你设计一个支付系统的安全方案你会怎么做谢飞机JWT就是那个很长的token分成Header、Payload、Signature三部分。OAuth2是授权协议主要用来授权第三方访问用户资源有授权码模式、密码模式这些。支付系统的话用OAuth2的client credentials模式Spring Security配一下然后JWT做身份令牌再用RSA非对称加密签名防止篡改。有效期设短一点。面试官老稳那怎么防止JWT重放攻击服务端如何吊销一个已经签发的JWT谢飞机啊重放攻击的话...给token加一个随机数nonce然后在Redis里存一下检测到重复就不通过。吊销的话可以搞一个黑名单把注销掉的jti存起来或者等token自己过期。面试官老稳听得出你了解一些但没有形成体系。那看看你排障能力。线上JVM内存一直在涨你怎么排查OOM谢飞机先jps找进程然后jmap -dump:formatb,fileheap.hprof pid导堆快照再用MAT或者VisualVM分析。最好启动时加上-XX:HeapDumpOnOutOfMemoryErrorOOM的时候自动生成dump文件。如果是CPU飙高就top -Hp pid找出耗CPU的线程号转成16进制然后jstack看线程栈。面试官老稳如果是频繁Full GC导致CPU飙升呢谢飞机那可能堆里有大对象或者内存泄漏。可以先看GC日志然后用jstat -gcutil看GC频率最后dump堆分析。不行的话就...加内存加机器换更大的堆哈哈。面试官老稳最后问个框架问题。Spring WebFlux和Spring MVC有什么区别你为什么不用WebFlux谢飞机WebFlux是响应式、非阻塞的底层是Netty基于事件循环MVC是传统的Servlet API一个请求占一个线程阻塞模型。WebFlux适合I/O密集型场景能省线程但学习成本高调试困难比如不要随便在响应式链路里用ThreadLocal。我们团队没人会用所以还是老实MVC。面试官老稳好吧。看了看手表语速平稳地说今天问题也问了不少。你先回去等通知吧我们还有几个候选人要面。谢飞机啊好...好的。谢谢面试官我站起来差点被双肩包带倒尴尬地退出了会议室。走廊里谢飞机掏出手机打开笔记默默记下了一行字“回去得把这些面试题都背下来不然下次又凉了。”面试问题详细答案与技术要点面试官的问题看似零散实际上环环相扣从基础到业务场景再到运维安全一步步深入。下面把三轮问题的答案详细拆解供新手系统学习。第一轮基础技术1. Java 8 / 11 / 17 核心特性Java 8正式引入Lambda表达式、函数式接口、Stream API、Optional、新的日期时间APIjava.time。Lambda简化了匿名内部类Stream提供了类似SQL的链式集合操作filter过滤、map转换、collect聚合、reduce汇总。Java 11最重要的长期支持版本。新增var局部变量推断只能用于局部变量不能用于字段和方法参数。新增标准HTTP Clientjava.net.http.HttpClient支持HTTP/2和WebSocket替代旧的HttpURLConnection。字符串新方法isBlank()、strip()、repeat()等。Java 17又一个LTS版本。正式引入Sealed Class密封类用sealed修饰类配合permits明确允许哪些子类继承限制了继承边界让领域模型更加严谨。还有instanceof模式匹配增强、switch表达式增强Java 14正式以及Foreign Function Memory API、Vector API等孵化特性。业务场景在大厂的存量系统里Java 8依然占据绝对主流但新项目会逐步迁移到17。面试时能说出“Java 8的StreamLambaJava 11的HttpClientvarJava 17的Sealed Class”已经足够展示你关注版本演进。2. Spring Boot 自动配置原理Spring Boot的“自动化”核心是SpringBootApplication它由三个注解组成SpringBootConfiguration继承Configuration表示这是一个配置类。EnableAutoConfiguration开启自动配置机制。ComponentScan扫描当前包及其子包下的Component等组件。EnableAutoConfiguration的实现原理通过Import(AutoConfigurationImportSelector.class)导入一个选择器。AutoConfigurationImportSelector会从依赖的jar包中读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件早期是spring.factories。文件里列出了所有候选的自动配置类如RedisAutoConfiguration、DataSourceAutoConfiguration。每个配置类上都有条件注解例如ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty。只有满足条件时对应的Bean才会被注册到Spring容器中。举例你引入spring-boot-starter-web后类路径下有DispatcherServlet和Servlet容器相关类WebMvcAutoConfiguration生效自动注册Controller、视图解析器、静态资源处理器等。关键点面试时说出“条件注解”是加分项。ConditionalOnMissingBean允许你用自定义Bean覆盖默认配置。3. MyBatis vs JPAHikariCP高性能原理MyBatis半自动ORM。需要手写SQLSQL与Java代码分离通过Mapper XML注解。适合复杂查询、报表、多表关联、SQL优化可控。缺点简单CRUD也需要写SQL映射规则需要维护。JPA / Hibernate全自动ORM。通过实体类注解映射表结构JpaRepository接口自动生成增删改查。适合领域模型驱动、CRUD为主的项目。缺点复杂查询容易生成低效SQL需要花精力调优。业务选型如果团队SQL功底强、业务以查询报表为核心优先MyBatis如果业务简单、快速迭代、要求高生产力选JPA。现在也有Spring Data JDBC这种中间选择。HikariCP为什么快字节码精简库体积只有几百KB代码优化到极致。FastList替代ArrayList来管理数据库连接避免ArrayList的边界检查、删除性能差等问题。ConcurrentBag自定义无锁并发集合利用ThreadLocal线程本地存储减少连接竞争。获取连接时优先拿本地线程的池极大降低锁开销。优化的默认配置如maximumPoolSize默认10minimumIdle与最大一致避免频繁创建连接。第二轮电商场景下的分布式问题4. Redis 缓存穿透、击穿、雪崩及解决方案缓存穿透查询一个不存在的key每次都会绕过缓存打到数据库。解决方案缓存空值即使DB查不到也把null写缓存过期时间设置短一些如3~5分钟。布隆过滤器把可能存在的数据ID放入布隆过滤器请求进缓存前先用布隆过滤器过滤不存在的ID直接短路。缓存击穿一个热点key在某个时间点过期大量并发请求同时去DB查询。解决方案互斥锁查询DB前尝试获取分布式锁如Redis的SETNX未获取锁的线程短暂重试避免同时打DB。逻辑过期不设置物理过期时间而是在value中存储过期时间戳异步线程负责更新缓存。热点key自动续期后台任务在热点key过期前主动更新。缓存雪崩大量key在同一时间过期或者缓存节点宕机导致流量全部涌向DB。解决方案过期时间加上随机值比如加1~5分钟随机偏移避免集体失效。使用Redis集群哨兵保证高可用。多级缓存本地缓存Caffeine分布式缓存Redis本地缓存扛压力。服务降级开启限流、熔断保护DB。5. Kafka 消息可靠性保证与幂等消费生产者不丢消息设置acksall或acks-1要求分区leader和所有ISR副本都写入成功才返回确认。设置retries和enable.idempotencetrue防止网络重试导致消息重复。使用事务保证跨分区原子性。broker不丢消息主题设置replication.factor 3保证数据有多个副本。设置min.insync.replicas 2最少有2个副本同步否则拒绝写入。使用Kafka Controller和ISR机制选举新leader保证高可用。消费者不丢消息关闭自动提交enable.auto.commitfalse改为手动提交offset。先处理业务逻辑再提交offset。如果业务处理成功但提交失败消费者重启后会再次消费需要幂等。幂等消费实现方案数据库唯一键插入记录时使用唯一索引如订单ID、消息ID重复插入失败。RedisSETNX用消息ID作为key若SETNX返回1说明第一次处理否则丢弃。状态机订单状态表里记录当前状态只有合法状态流转才能更新重复消息不影响最终一致性。6. Eureka vs ConsulOpenFeign超时Resilience4jEurekaAP模型。服务注册后各个节点间异步复制不保证强一致但在网络分区时依然可用。适合追求高可用的场景比如下单、支付。ConsulCP模型基于Raft协议数据强一致。但发生分区时为了一致性会牺牲可用性。适合对一致性要求高的服务如配置管理、服务发现。Nacos题外扩展既能做AP也能做CP动态切换国内大厂使用广泛。OpenFeign超时设置OpenFeign集成了Ribbon或LoadBalancer但Feign自己也定义了超时参数在Spring Cloud中正确配置方式是feign: client: config: default: connectTimeout: 5000 # 连接超时 readTimeout: 10000 # 读取超时如果使用Ribbon还需要配置ribbon.ConnectTimeout和ribbon.ReadTimeout但更推荐直接在Feign的config里控制。Resilience4j一个轻量级容错库替代Hystrix。提供熔断CircuitBreaker、限流RateLimiter、重试Retry、隔离Bulkhead、限时TimeLimiter等组件。Spring Boot 2.4以后通过spring-cloud-starter-circuitbreaker-resilience4j集成业务可以用注解或编程式API实现降级。第三轮安全、JVM与响应式7. JWT 和 OAuth2 的区别支付系统安全设计JWT一种跨域认证的令牌格式。由Header算法信息、Payload自定义声明、Signature用密钥或私钥对前两部分的签名三段组成。JWT本身不是授权协议只是令牌的载体。OAuth2一种授权框架解决“第三方应用如何获得用户资源访问权限”的问题。它有四种模式授权码模式、简化模式、密码模式、客户端凭证模式。OAuth2会给第三方发放access_token可以是JWT也可以是不透明字符串还可以用refresh_token刷新访问令牌。区别JWT是一种Token实现OAuth2是一种授权流程。OAuth2签发的Token可以用JWT来编码。两者不冲突通常组合使用先通过OAuth2获取JWT然后在各服务间用JWT做身份认证。支付系统安全方案要点使用OAuth2的client_credentials模式或authorization_code模式配合Spring Security OAuth2/JWT。JWT签名使用RS256非对称加密公钥放网关私钥保存在认证服务器防止伪造。设置较短的token有效期比如15分钟配合refresh_token实现无感刷新。防止重放攻击在JWT中加入jti唯一ID和iat签发时间服务端将已使用的jti或者nonce存入Redis下次遇到相同jti直接拒绝。吊销机制维护JWT黑名单Redis Set将已注销、被踢出的用户jti加入黑名单并设置与JWT过期时间一致的TTL。传输安全必须使用HTTPS防止Token被中间人截获。8. JVM 内存泄漏与 OOM 排查排查流程确认进程IDjps列出Java进程。获取GC状态jstat -gcutil pid 1000 10实时查看Eden、Old区使用率和Full GC次数及耗时。导出堆快照jmap -dump:formatb,fileheap.hprof pid或者启动参数加-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/logs/让JVM在OOM时自动dump。分析快照用MATMemory Analyzer、VisualVM、JProfiler。主要看堆上最大的对象Dominators。垃圾回收根路径GC Roots。是否出现大量相同线程的ThreadLocal、未关闭的连接、静态集合等。定位内存泄漏代码找到生命周期比预期长的对象比如一个全局static List只加不删或者数据库中连接未释放。High CPU / 频繁 Full GC 排查top -Hp pid查看Java进程内部各个线程的CPU占用找到CPU最高的线程号。使用printf %x\n 线程号转换为十六进制。jstack pid | grep -A 20 0xhex查看该线程栈定位是垃圾回收线程还是业务线程。如果是GC线程占高CPU结合jstat确认是否频繁Full GC再用jmapdump堆分析大对象、内存泄漏。切忌直接“加内存”这只会掩盖问题。9. Spring WebFlux 与 Spring MVC 对比| 维度 | Spring MVC | Spring WebFlux | |------|-----------|----------------| | 模型 | 每请求一个线程同步阻塞I/O | 事件循环少量线程处理高并发非阻塞I/O | | 底层 | Servlet APITomcat、Jetty | Netty、Reactor Netty | | 编程方式 | 注解式Controller方法阻塞返回对象 | 注解式Controller方法返回Mono/Flux也可以使用函数式Routers | | 并发能力 | 高并发下线程池耗尽风险 | 线程数固定I/O等待时不占线程适合少线程扛高并发 | | 学习难度 | 低 | 高需要理解响应式编程、背压Backpressure | | 适用场景 | 传统项目、CPU密集型、阻塞型框架MyBatis、JPA原生等 | API网关、高并发低延迟I/O密集型场景、长连接推送 | | 兼容性 | 大量SDK都是阻塞的集成容易 | 阻塞库如JDBC不能直接用需要R2DBC或非阻塞客户端 |为什么不轻易用WebFluxJDBC是阻塞API如果你用MyBatis/JPA在WebFlux里调用会导致线程阻塞反而更差。除非改用R2DBC但生态不如JDBC成熟。调试困难响应式调用链是异步的堆栈信息难以阅读网上资料也少。对团队要求高大厂里如果不是专职中间件或高并发网关普遍还是用Spring MVC配合调优和缓存来支撑大流量。面试官最后的那句“回去等通知”基本等于“不希望通过”。但谢飞机并不气馁因为面试本来就是查漏补缺的过程。以上这些答案从原理到场景都拆解清楚了真正吃透之后下一次面试也许就能拿到offer了。祝所有“谢飞机”们都准备充分再战。