阅读指南

原则:代码是你写的,看一眼就能想起来。每道题 5 分钟足够。


一、Spring 核心(3 题)

1. Spring Boot 自动配置原理?(文件:main.ts / DemoApplication

一句话@SpringBootApplication 包含 @EnableAutoConfigurationAutoConfigurationImportSelector 扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports → 按条件注解加载配置类。

你项目里:MybatisPlusAutoConfigurationRedisAutoConfigurationSecurityAutoConfiguration 都是这么加载的。

追问 “条件注解有哪些?”@ConditionalOnClass(有 Redisson 才加载)、@ConditionalOnMissingBean(用户没自定义才加载)、@ConditionalOnProperty


2. Bean 生命周期?(文件:SecurityConfig.java

一句话:实例化 → 属性注入 → BeanPostProcessor.postProcessBeforeInitialization()@PostConstructInitializingBean.afterPropertiesSet()@BeaninitMethodBeanPostProcessor.postProcessAfterInitialization() → 容器关闭时 @PreDestroy / DisposableBean.destroy()

你项目里:SecurityConfig.@PostConstructSecurityContextHolder.setStrategyName(...) 就是个生命周期钩子。

追问 “单例 Bean 线程安全吗?”:不安全,因为多个线程共享同一个实例。用 ThreadLocal(如 RequestContextHolder)或无状态设计来解决。


3. AOP 原理?(文件:RateLimitAspect.java

一句话:JDK 动态代理(接口)vs CGLIB(类继承)。Spring 默认用 CGLIB。@Around 包裹目标方法,joinPoint.proceed() 才真正执行。

你项目里:RateLimitAspect 是个标准 @Around 切面。Lua 脚本校验通过 → joinPoint.proceed() 放行;失败 → 调 fallback 方法或抛异常。

追问 “多个切面的执行顺序?”@Order 控制,值越小越外层。没有显式声明时,Spring 内部按 AspectJAutoProxyRegistrar 的规则排序。


二、Spring Security + JWT(2 题)

4. JWT 认证流程?(文件:SecurityConfig.java + JwtAuthenticationFilter.java

1
2
3
4
5
用户登录 → 服务端验证密码 → 签 JWT(header.payload.signature)
→ 返回给前端 → 前端 localStorage 存
→ 后续请求 Axios 拦截器自动带 Authorization: Bearer <token>
→ 后端 JwtAuthenticationFilter 验证签名、解析 userId
→ 写入 SecurityContextHolder

你项目里:SessionCreationPolicy.STATELESS 关掉 Session。SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 让异步线程也能拿到认证上下文。

追问 “JWT 踢人下线怎么做?”:无状态模式下没法直接踢。方案:Redis 维护黑名单(logout 时加入),JwtFilter 查黑名单来拒绝。


5. CSRF 为什么关了?(文件:SecurityConfig.java

你项目里:.csrf(csrf -> csrf.disable())

:因为前后端分离 + JWT 无状态认证,不依赖 Cookie,CSRF 攻击的条件(攻击者利用浏览器自动带 Cookie)不存在。API 只走 Authorization header,跨站请求带不了这个头。


三、Redis(3 题)

6. 缓存穿透/击穿/雪崩?(文件:CacheThroughService.java

问题 现象 你的解决方案
穿透 查不存在的 key,每次穿透到 DB 没做布隆过滤器(项目里有 bloom/ 目录但未集成)
击穿 热点 key 过期瞬间,大量请求打到 DB CacheThroughService:分布式锁 + 双重检查 + 兜底直查
雪崩 大量 key 同时过期,DB 瞬间压力 CacheTTL 随机过期时间(base ± random)

追问 “Redisson 锁怎么防止死锁?”tryLock(waitTime, leaseTime, TimeUnit)leaseTime(你设了 30s)到期自动释放;底层 Watchdog 每 10s 续期。


7. Redisson 分布式锁原理?(文件:CacheThroughService.java + GradingStreamConsumer.java

1
2
RLock lock = redissonClient.getLock("lock:grading:" + submissionId);
lock.tryLock(0, 180, TimeUnit.SECONDS);

流程:Redisson 用 Redis Hash + Lua 脚本原子加锁。Watchdog 每 10s 续期 30s。释放时检查 isHeldByCurrentThread()(用 UUID + threadId 标识持有人),防止释放别人的锁。

synchronized 的区别synchronized 只锁当前 JVM,多实例部署改了就失效。Redisson 跨 JVM 互斥。


8. Redis Stream vs Kafka?(文件:AbstractStreamConsumer.java + AsyncTaskConstants.java

Redis Stream Kafka
持久化 AOF/RDB 持久化 磁盘持久化,更可靠
消费模型 消费者组,ACK 确认 消费者组,offset 提交
消费速度跟不上 消息积压,需扩容 consumer 天然支持分区并行
适用场景 轻量异步任务(批改队列) 高吞吐日志/事件流

你选 Redis Stream 的理由:量不大(课堂级 150 并发),无需引入额外中间件,和缓存共用一个 Redis 降低运维复杂度。

追问 “消息可靠性”:AOF everysec 每秒刷盘 → 最多丢 1 秒消息;消费者 ACK 确认 → 只删已确认消息;Pending 认领 → 消费者崩溃可用其他 consumer 认领。


四、并发/线程(2 题)

9. Daemon 线程 vs 用户线程?(文件:AbstractStreamConsumer.java

1
2
Thread t = new Thread(r, threadName());
t.setDaemon(true);

:JVM 在所有用户线程结束后才退出,Daemon 线程不阻止 JVM 退出。消费者设 Daemon 是因为 Spring Boot 关闭时不需要等它优雅完成,应用退出即可。


10. ConcurrentHashMap vs HashMap?(文件:McpController.java

你的 tools Map 是 LinkedHashMap

1
private final Map<String, ToolDefinition> tools = new LinkedHashMap<>();

面试官问:多线程安全吗?

:构造函数里初始化,之后只 get() 不修改,所以线程安全。如果需要运行时动态注册工具,需改 ConcurrentHashMap

追问 “ConcurrentHashMap 原理(Java 21)”:Java 8+ 用 CAS + synchronized 锁链表头节点。Java 21 没有本质变化,主要优化在虚拟线程的 synchronized 不 pin 住载体线程。


五、MyBatis-Plus(1 题)

11. MyBatis-Plus vs JPA vs 原生 MyBatis?(文件:pom.xml + Mapper 文件)

:MyBatis-Plus 在原生 MyBatis 上加了内置 CRUD(BaseMapper)、条件构造器(LambdaQueryWrapper)、分页插件。和 JPA 相比:SQL 完全可控(复杂查询直接写 SQL),没有 N+1 隐式查询问题。

你项目里:StudentStatsTool.execute() 里用 LambdaQueryWrapper 做条件查询,类型安全 + 防字段名拼错。


六、设计模式(1 题)

12. 你项目里用了哪些设计模式?

模式 位置 代码
模板方法 AbstractStreamConsumer 消费循环固定模板,子类覆写钩子方法
策略 ToolDefinition 接口 每个 MCP Tool 是一种策略,统一接口
单例 所有 @Service / @Component Spring 容器默认单例
代理 RateLimitAspect AOP 动态代理增强
观察者 @Scheduled + StreamMonitor 定时检查 Stream 状态

七、快速复习顺序(按面试出现频率)

  1. 缓存三兄弟(穿透/击穿/雪崩)→ 看 CacheThroughService.java
  2. 分布式锁 → 看 GradingStreamConsumer.java 里的 RLock 用法
  3. JWT 认证流程 → 看 SecurityConfig.java + JwtAuthenticationFilter.java
  4. AOP 原理 → 看 RateLimitAspect.java
  5. 消息队列可靠性 → 看 AbstractStreamConsumer.java + AsyncTaskConstants.java
  6. 设计模式 → 模板方法(AbstractStreamConsumer)+ 策略(ToolDefinition)
  7. Bean 生命周期 → 随便搜一个 @PostConstruct
  8. ConcurrentHashMap → 看 McpController.java

最后

项目是你的护城河。八股忘了就忘了,看一眼代码就能捡回来——因为那是你写的。

别背,边看代码边说,自然就流畅。