前端视角:NestJS 和 Spring Boot,我靠对比学习啃下两个后端框架
Spring Boot 在 Java 技术栈里的地位我早有耳闻——但凡跟后端同事聊两句,十有八九项目跑的就是它。一个前端写多了业务,总会好奇:这个”霸主”到底长什么样。我是带着这个好奇去学的,顺便想把自己对后端的理解往前推一截,以后看业务、和后端协作时心里更有底。
我没直接啃 Java,而是先学了 NestJS,因为它是 TypeScript,前端上手快。学完发现它和 Spring Boot 长得像得离谱——同样是装饰器满天飞,同样有个容器帮你管对象。于是我反过来用 NestJS 当跳板摸 Spring Boot,一个没写过 Java 的人,靠着这条路径也算摸出了它的大致骨架。
这篇记一下”对比学习”这套方法,以及这两个框架到底哪里相通、哪里只是看起来像。
先说方法:从已知映射未知
我不是第一次用这套方法。我是 Vue 技术栈出身,学 React 的时候,就是从 Vue 映射过去的:ref/reactive 对应 useState,computed 对应 useMemo,watch 对应 useEffect。映射建立之后,新框架的大部分就不用从零学了,剩下真正陌生的部分才是要花时间死磕的。
对比学习的前提是:新旧两样东西在解决同一类问题。NestJS 和 Spring Boot 都是后端框架,都要解决”请求进来 → 路由 → 业务逻辑 → 数据访问 → 响应出去”这条链路。问题一样,解法就难免趋同,这给映射提供了土壤。
但也不是什么都能这么学。比如 Angular,我完全没碰过,手里没有任何老底可以映射,真要学也只能从零硬啃。能靠对比省力的,只是那些”我这边正好有对应经验”的部分——这次算我运气好,Nest 和 Spring 恰好是这种情况。
AI 让建映射变快了
以前建映射要自己两边翻文档,慢。现在我把已掌握的那个框架丢给 agent,让它当翻译:
- “我是前端,熟 NestJS。请用 NestJS 的概念给我讲 Spring Boot 的依赖注入,每条都给出 NestJS 里的对应物。”
- “写一段 Spring Boot 代码,故意用 NestJS 的习惯写错,让我找问题。”
- “我理解了这个注入方式,请当面试官追问我三个会暴露理解偏差的问题。”
agent 建的映射表只是草稿,不能照单全收。它经常把”形似”当成”神似”——比如会把两边某个注解直接划等号,但细看生命周期、作用域这些细节,其实不是一回事。这种地方得自己动手写一遍代码才会露馅。
AI 加速的是”建立对照”这一步,不是”判断对照对不对”。判断还在人。
两个框架真正相通的地方
下面这些是我摸下来对应得最实的部分。说实话,最先让我”啊,原来是这个意思”的,是横切关注这一块——大概是因为它跟前端的中间件、拦截逻辑离得最近,一下就有画面感,反倒是 IoC 容器那套,我是回头才慢慢理清楚的。
横切关注:拦截器、守卫、管道 vs 过滤器、切面
列个自己整理的对照表:
| 关注点 | NestJS | Spring Boot |
|---|---|---|
| 鉴权 | Guard | Filter / Spring Security |
| 请求/响应转换 | Interceptor | Interceptor |
| 参数校验/转换 | Pipe | @Valid + Converter |
| 异常处理 | Exception Filter | @ControllerAdvice |
| 横切日志 | Interceptor | 切面(@Aspect) |
两边的思路是一样的:不侵入业务代码,把”所有请求都要走一遍”的逻辑(鉴权、日志、参数校验)抽出来集中写,而不是散在每个业务方法里到处复制粘贴。前端写多了拦截逻辑(比如 axios 的请求/响应拦截器)对这套一点都不陌生,只是后端把它做得更彻底、更成体系。
理解这一层之后,后端同事说”加个切面统一处理”,我脑子里大概能对上号:NestJS 里我会写个 Interceptor,Spring 里他写个 @Aspect,落在请求链路上的位置差不多。
IoC 容器:东西放进盒子,要用时拿出来
NestJS:
1 | () |
Spring Boot:
1 |
|
@Injectable() 和 @Service 干的是同一件事:告诉容器”这是个可被注入的东西”,写业务代码的人不用自己去 new,用的时候容器会把实例递过来。NestJS 靠 @Module 里显式列一份 providers 清单,Spring 靠扫描注解自动找——一个手动登记,一个自动收集,但目的都是”把能用的东西丢进一个池子里管起来”。这块概念我是先在 Nest 里对着代码跑通一遍,再回头看 Spring 才算真的理清楚,一开始光看文字说明是没懂的。
装饰器:声明式地把元数据钉在代码上
NestJS 的控制器:
1 | ('users') |
Spring Boot:
1 |
|
对照几乎是逐行的:用一个标记说”这是个路由”,用另一个标记说”这个参数从 URL 里取”,框架在运行时读出来帮你接线,你不用自己写解析逻辑。
我查资料的时候看到说 NestJS 这套装饰器风格是从 Angular 那边搬过来的——Angular 我没用过,具体渊源说不上来,但单看这种”用标记描述意图,让框架做机械活”的思路,跟 Vue 里 <script setup> 的一些编译期魔法、React 的高阶组件其实是同一个路数,前端看到不会太陌生。
生命周期钩子
NestJS 的 OnModuleInit、OnApplicationBootstrap;Spring 的 @PostConstruct。都是”对象创建完/应用启动完”这类时机的回调。前端类比:Vue 的 mounted、React 的 useEffect,同一种”框架告诉我到点了”的机制,这个映射基本不用怎么想就能对上。
哪里只是看起来像
对比学习最容易翻车的地方,是只看到像、看不到不像。我刻意记了几条,免得自己回头拿 NestJS 的经验去套 Spring 出问题:
运行时不一样。 NestJS 跑在 Node 上,单线程,异步靠 async/await。Spring 跑在 JVM 上,是多线程模型。这意味着 NestJS 里一个请求处理阻塞了,会连累后面排队的请求;Spring 那边一个请求占一个线程,互相干扰小一些。写拦截逻辑的时候这个差异会直接咬人——NestJS 的拦截器里如果塞了同步的重活,容易拖慢整个服务。
依赖注入的实现方式不一样。 NestJS 靠 TypeScript 的装饰器 + 元数据,没标 @Injectable() 的东西是注入不进去的;Spring 靠 Java 的反射机制,能拿到的运行时信息更多。这个差异导致两边”动态注入一组东西”的写法不太一样,具体细节我也还在慢慢摸。
生态成熟度不一样。 Spring Boot 更像”全家桶”,数据层、安全、多环境配置基本都有官方现成的方案,跟着官方文档走大概率不会错。NestJS 更像”核心 + 自己拼”,数据库要自己在 TypeORM/Prisma 里选,鉴权要自己接 Passport——这点上反而更像前端的习惯,什么都得自己挑生态。Spring 官方给的答案多,NestJS 的自由度更高,两种风格没有绝对的好坏,适应成本不一样而已。
对比学习不是省力,是省时间
对比学习给我的不是”少学”,而是”学得快”。Spring Boot 那些核心概念本来就要学,但因为 NestJS 里我已经摸过一遍类似的东西,又是熟悉的 TypeScript,所以能借这条路径把 Spring 的骨架搭得快一些。真正要花时间死磕的,是那些映射不上、或者一开始映射错了的部分——而这部分往往才是真正值得学的。
AI agent 在这中间扮演的是”快速出题人 + 对照表初稿生成器”的角色。它能让我把两边的代码放在一起跑一遍看效果,省了不少来回翻文档的时间。但它给的所有对照我都得自己动手验一遍,因为它经常分不清”形似”和”神似”,草稿信不得,得自己较真。
后端同事再讲架构,我至少能接上一部分话了——不是因为我会写 Java 了,是因为我手里多了一张不算精确但能用的映射图。前端去啃一个陌生的后端框架,最大的收获不是多会一门语言,是多少看清了”一个请求在服务端到底经历了什么”。这张图反过来对我写前端也有点用:知道后端大概是 Controller 接请求、Service 写业务、Repository 管数据这么一路传下去之后,前端这边怎么组织接口调用层,多少更有谱一点。