Spring Boot面试实战:面试官与“水货“程序员谢飞机的巅峰对决(含微服务/数据库/缓存高频考点) Spring Boot面试实战面试官与水货程序员谢飞机的巅峰对决 本文以故事化的方式带你沉浸式体验一场Java技术面试。严肃面试官 vs 搞笑水货程序员谢飞机3轮技术对决涵盖Spring Boot、微服务、数据库、缓存等核心考点 人物介绍| 角色 | 描述 | |------|------| | 王面试官| 某互联网大厂技术面试官10年Java开发经验以严厉著称 | | 谢飞机| 自称全栈工程师的求职者简历写得天花乱坠实际水平...第一轮Spring Boot 基础与原理 面试开始王面试官谢飞机是吧看你简历上写着精通Spring Boot那我问你Spring Boot的自动配置原理是什么谢飞机自信满满这个我太熟了Spring Boot自动配置就是...就是...它自己就配好了你不用管它怎么配的反正能用就行王面试官扶额...那你告诉我SpringBootApplication这个注解包含了哪些注解谢飞机嗯...包含Configuration...还有一个Enable...什么的...反正三个注解组合在一起就对了王面试官最后一个问题Spring Boot的starter机制了解吗谢飞机starter嘛就是起步依赖加了就能用 技术深度解析正确答案SpringBootApplication是一个组合注解包含SpringBootConfiguration // 等同于 Configuration EnableAutoConfiguration // 开启自动配置 ComponentScan // 组件扫描 public interface SpringBootApplication { // ... }自动配置原理核心流程1. EnableAutoConfiguration 开启自动配置 2. 通过 Import(AutoConfigurationImportSelector.class) 导入选择器 3. 读取 META-INF/spring.factories 文件Spring Boot 2.x 或 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring Boot 3.x 4. 根据 Conditional 条件注解判断是否生效 5. 完成自动配置自定义Starter示例// 1. 定义配置属性类 ConfigurationProperties(prefix my.service) public class MyServiceProperties { private String prefix [MyService]; private String suffix !; // getters and setters... } // 2. 定义自动配置类 AutoConfiguration ConditionalOnClass(MyService.class) EnableConfigurationProperties(MyServiceProperties.class) public class MyServiceAutoConfiguration { Bean ConditionalOnMissingBean public MyService myService(MyServiceProperties properties) { return new MyService(properties); } } // 3. 业务服务类 public class MyService { private final MyServiceProperties properties; public MyService(MyServiceProperties properties) { this.properties properties; } public String wrap(String word) { return properties.getPrefix() word properties.getSuffix(); } }第二轮微服务架构与分布式 面试继续王面试官行吧Spring Boot算你过了。那你简历上写的微服务架构说说你们项目的微服务是怎么划分的谢飞机我们项目可大了有用户服务、订单服务、商品服务...反正就是很多服务王面试官那服务之间怎么通信的谢飞机用Feign就是...就是写个接口加个注解就能调了王面试官服务注册与发现用的什么谢飞机Nacos就是那个阿里巴巴的...注册中心服务启动就注册上去了王面试官那如果服务调用失败了怎么办有做熔断降级吗谢飞机沉默3秒这个...我们一般不失败... 技术深度解析微服务核心架构图┌─────────────┐ │ Gateway │ ← Spring Cloud Gateway └──────┬──────┘ │ ┌────────────┼────────────┐ │ │ │ ┌─────▼─────┐ ┌──▼──────┐ ┌──▼──────┐ │ User │ │ Order │ │ Product │ │ Service │ │ Service │ │ Service │ └─────┬─────┘ └──┬──────┘ └──┬──────┘ │ │ │ └──────────┼───────────┘ │ ┌──────▼──────┐ │ Nacos │ ← 注册中心 配置中心 └─────────────┘OpenFeign 服务调用示例// 定义Feign客户端 FeignClient(name product-service, fallbackFactory ProductClientFallbackFactory.class) public interface ProductClient { GetMapping(/api/product/{id}) ResultProductDTO getProduct(PathVariable(id) Long id); PostMapping(/api/product/stock/deduct) ResultBoolean deductStock(RequestBody StockDTO stockDTO); } // 降级处理 Component public class ProductClientFallbackFactory implements FallbackFactoryProductClient { Override public ProductClient create(Throwable cause) { return new ProductClient() { Override public ResultProductDTO getProduct(Long id) { log.error(调用商品服务失败触发降级: {}, cause.getMessage()); return Result.fail(商品服务暂时不可用); } Override public ResultBoolean deductStock(StockDTO stockDTO) { log.error(扣减库存失败触发降级: {}, cause.getMessage()); return Result.fail(库存扣减失败请稍后重试); } }; } }Sentinel 熔断降级配置Configuration public class SentinelConfig { PostConstruct public void initRules() { ListFlowRule rules new ArrayList(); // 流控规则QPS超过100时触发限流 FlowRule rule new FlowRule(); rule.setResource(getProduct); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(100); rules.add(rule); FlowRuleManager.loadRules(rules); // 熔断规则慢调用比例超过50%响应时间超过3秒 ListDegradeRule degradeRules new ArrayList(); DegradeRule degradeRule new DegradeRule(); degradeRule.setResource(getProduct); degradeRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()); degradeRule.setCount(0.5); // 慢调用比例50% degradeRule.setSlowRatioThreshold(3000); // 慢调用阈值3秒 degradeRule.setTimeWindow(10); // 熔断时长10秒 degradeRules.add(degradeRule); DegradeRuleManager.loadRules(degradeRules); } }第三轮数据库与缓存 终极对决王面试官最后一轮数据库和缓存。先说说MySQL的索引类型有哪些谢飞机这个我知道有主键索引、普通索引、唯一索引...还有一个...全文索引王面试官那联合索引的最左前缀原则是什么谢飞机最左前缀嘛...就是从左边开始匹配...如果左边没有就用不了索引王面试官Redis常用的数据结构有哪些谢飞机StringHashListSet还有一个Z...Z什么来着...王面试官ZSet有序集合。那缓存穿透、缓存击穿、缓存雪崩你怎么解决谢飞机满头大汗这个...缓存穿透就是...缓存没有...然后请求都打到数据库... 技术深度解析MySQL索引详解-- 创建联合索引 CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time); -- ✅ 能走索引的查询符合最左前缀 SELECT * FROM orders WHERE user_id 1; SELECT * FROM orders WHERE user_id 1 AND status PAID; SELECT * FROM orders WHERE user_id 1 AND status PAID AND create_time 2026-01-01; -- ❌ 不能走索引的查询跳过了user_id SELECT * FROM orders WHERE status PAID; SELECT * FROM orders WHERE status PAID AND create_time 2026-01-01; -- ✅ 能走索引MySQL 8.0 索引跳跃扫描 SELECT * FROM orders WHERE status PAID;Redis缓存三大问题及解决方案Service public class CacheService { Autowired private StringRedisTemplate redisTemplate; Autowired private ProductMapper productMapper; // 1. 缓存穿透解决方案 // 方案A缓存空值 public Product getProductWithNullCache(Long id) { String key product: id; String json redisTemplate.opsForValue().get(key); // 缓存了空值直接返回 if (NULL.equals(json)) { return null; } if (json ! null) { return JSON.parseObject(json, Product.class); } // 查数据库 Product product productMapper.selectById(id); if (product null) { // 缓存空值设置较短过期时间 redisTemplate.opsForValue().set(key, NULL, 5, TimeUnit.MINUTES); return null; } redisTemplate.opsForValue().set(key, JSON.toJSONString(product), 30, TimeUnit.MINUTES); return product; } // 方案B布隆过滤器 Bean public BloomFilterString bloomFilter() { BloomFilterString bloomFilter BloomFilter.create( Funnels.stringFunnel(Charset.forName(UTF-8)), 1000000, // 预期数据量 0.03 // 误判率 ); // 初始化加载所有商品ID productMapper.selectAllIds().forEach(id - bloomFilter.put(String.valueOf(id)) ); return bloomFilter; } // 2. 缓存击穿解决方案 // 方案互斥锁分布式锁 public Product getProductWithLock(Long id) { String key product: id; String json redisTemplate.opsForValue().get(key); if (json ! null) { return JSON.parseObject(json, Product.class); } // 获取分布式锁 String lockKey lock:product: id; String lockValue UUID.randomUUID().toString(); try { Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { // 双重检查 json redisTemplate.opsForValue().get(key); if (json ! null) { return JSON.parseObject(json, Product.class); } // 查数据库并写缓存 Product product productMapper.selectById(id); if (product ! null) { redisTemplate.opsForValue().set(key, JSON.toJSONString(product), 30, TimeUnit.MINUTES); } return product; } else { // 未获取到锁短暂休眠后重试 Thread.sleep(50); return getProductWithLock(id); } } finally { // 释放锁Lua脚本保证原子性 String luaScript if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; redisTemplate.execute(new DefaultRedisScript(luaScript, Long.class), Collections.singletonList(lockKey), lockValue); } } // 3. 缓存雪崩解决方案 // 方案随机过期时间 多级缓存 public void setCacheWithRandomExpire(String key, String value) { // 基础过期时间 随机时间避免同时过期 int baseExpire 30; // 30分钟 int randomExpire ThreadLocalRandom.current().nextInt(0, 10); // 0-10分钟随机 redisTemplate.opsForValue().set(key, value, baseExpire randomExpire, TimeUnit.MINUTES); } // 本地缓存 Redis 多级缓存 private final CacheString, Product localCache Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES) .build(); public Product getProductMultiLevel(Long id) { String key product: id; // L1: 本地缓存 Product product localCache.getIfPresent(String.valueOf(id)); if (product ! null) { return product; } // L2: Redis缓存 String json redisTemplate.opsForValue().get(key); if (json ! null) { product JSON.parseObject(json, Product.class); localCache.put(String.valueOf(id), product); return product; } // L3: 数据库 product productMapper.selectById(id); if (product ! null) { localCache.put(String.valueOf(id), product); setCacheWithRandomExpire(key, JSON.toJSONString(product)); } return product; } } 面试结果王面试官合上简历谢飞机你的面试表现...怎么说呢理论知道一些但深度不够。很多概念停留在表面缺乏实际项目经验的积累。谢飞机王哥那我...王面试官建议回去把Spring Boot自动配置源码好好看看微服务的熔断降级一定要动手实践Redis缓存三大问题的解决方案要能写出代码。我们后续有结果会通知你的。谢飞机内心OS完了完了这次又凉了... 面试高频考点总结| 分类 | 高频考点 | 重要程度 | |------|----------|----------| | Spring Boot | 自动配置原理、Starter机制、条件注解 | ⭐⭐⭐⭐⭐ | | 微服务 | 服务注册发现、Feign调用、熔断降级、网关 | ⭐⭐⭐⭐⭐ | | MySQL | 索引原理、最左前缀、事务隔离级别、MVCC | ⭐⭐⭐⭐⭐ | | Redis | 数据结构、持久化、缓存穿透/击穿/雪崩 | ⭐⭐⭐⭐⭐ | | 分布式 | 分布式锁、分布式事务、CAP理论 | ⭐⭐⭐⭐ | 写在最后面试不是背八股文而是要真正理解技术背后的原理。希望谢飞机的故事能给大家带来启发知其然更要知其所以然— 不要只停留在能用就行动手实践比死记硬背更重要— 代码写出来才是自己的项目经验是加分项— 把技术用到实际场景中 如果这篇文章对你有帮助别忘了点赞、收藏、关注三连哦我们下期见作者不嘻嘻~ | 专注于Java技术分享与面试经验总结