从 Spring Boot 到 NestJS:项目快速上手指南
📅 2026/7/8 3:36:36
👁️ 阅读次数
📝 编程学习
从 Spring Boot 到 NestJS:项目快速上手指南
本文档专为具有 Spring Boot 开发经验的开发者编写,旨在通过类比和循序渐进的实战指导,帮助你快速熟悉并参与到当前 NestJS 项目的开发中。
第一阶段:全局宏观认知
1. 架构思想的无缝平移
NestJS 几乎完美复刻了 Spring Boot 的架构哲学:
- IoC/DI (控制反转与依赖注入):默认单例,通过构造函数注入。
- AOP (面向切面编程):拦截器、守卫、异常过滤器一应俱全。
- 模块化 (Module):按业务划分模块,避免代码耦合。
2. 核心装饰器(注解)对照表
| Spring Boot | NestJS | 作用说明 |
|---|---|---|
@RestController | @Controller() | 标记控制器,定义路由前缀 |
@Service/@Component | @Injectable() | 标记为 Provider,交由 IoC 容器管理 |
@Autowired | (无需显式注解) | 直接在类的constructor中声明即可自动注入 |
@GetMapping("/path") | @Get('path') | GET 请求路由映射 |
@PostMapping | @Post() | POST 请求路由映射 |
@RequestBody | @Body() | 获取请求体 JSON 数据 |
@RequestParam | @Query() | 获取 URL 查询参数 (?key=value) |
@PathVariable | @Param() | 获取路径参数 (/api/:id) |
第二阶段:熟悉项目目录结构
当前项目的结构与标准的 MVC 分层高度一致:
src/ ├── main.ts # 等同于 Application.java,项目启动入口 ├── app.module.ts # 根模块,等同于 @SpringBootApplication + 全局配置 ├── config/ # 配置文件目录(等同于 application.yml) ├── common/ # 通用工具类、拦截器、全局异常处理等 │ ├── base/ # 基础 Controller/Service 类(可继承复用) │ ├── decorators/ # 自定义装饰器(自定义注解) │ ├── interceptors/ # 拦截器(如统一返回值包装 transform.interceptor.ts) │ └── filters/ # 异常过滤器(全局异常捕获) ├── module/ # 业务模块目录 │ ├── ota/ # OTA核心业务模块 │ │ ├── xxx.controller.ts # 控制层 │ │ ├── xxx.service.ts # 业务逻辑层 │ │ ├── xxx.entity.ts # 数据库实体类 (等同于 @Entity) │ │ ├── xxx.module.ts # 模块组装文件 (必须将Controller和Service注册在此) │ │ └── dto/ # 数据传输对象 (入参/出参校验)第三阶段:ORM 与数据库操作 (TypeORM)
本项目使用TypeORM,体验上非常接近JPA和Hibernate。
1. 实体定义 (Entity)
类似 JPA,通过装饰器映射表结构:
import{Entity,Column,PrimaryGeneratedColumn}from'typeorm';@Entity('sys_user')// @Table(name = "sys_user")exportclassUser{@PrimaryGeneratedColumn()// @Id @GeneratedValueid:number;@Column({name:'user_name',length:50})// @ColumnuserName:string;}2. 数据库操作 (Repository)
类似于JpaRepository,在 Service 中注入Repository即可进行 CRUD:
import{Injectable}from'@nestjs/common';import{InjectRepository}from'@nestjs/typeorm';import{Repository}from'typeorm';import{User}from'./user.entity';@Injectable()exportclassUserService{// 等同于 @Autowired private UserRepository userRepository;constructor(@InjectRepository(User)privatereadonlyuserRepository:Repository<User>,){}asyncfindUser(id:number){returnawaitthis.userRepository.findOne({where:{id}});}}第四阶段:实战演练(手把手写一个接口)
假设我们需要在src/module/ota/下新建一个获取航班列表的接口。请遵循单一职责和最小开发单元的规则。
Step 1: 定义入参 DTO (flight-query.dto.ts)
等同于 Java 的 Request 对象。使用class-validator进行校验(类似@Valid+javax.validation)。
import{IsString,IsNotEmpty}from'class-validator';exportclassFlightQueryDto{@IsString()@IsNotEmpty({message:'出发地不能为空'})depCity:string;@IsString()@IsNotEmpty({message:'目的地不能为空'})arrCity:string;}Step 2: 编写 Service (flight.service.ts)
实现核心业务逻辑。注意:优先使用卫语句(early return),复杂逻辑请拆分。
import{Injectable}from'@nestjs/common';import{FlightQueryDto}from'./dto/flight-query.dto';import{ObjectUtils}from'src/common/utils/object.util';// 假设有这个工具类@Injectable()exportclassFlightService{asyncgetFlightList(query:FlightQueryDto){// 卫语句:优先拦截异常情况if(ObjectUtils.isEmpty(query.depCity)||ObjectUtils.isEmpty(query.arrCity)){thrownewError('城市参数缺失');// 全局 Filter 会捕获并格式化抛出}// 业务逻辑处理...return[{flightNo:'CZ3201',dep:query.depCity,arr:query.arrCity}];}}Step 3: 编写 Controller (flight.controller.ts)
接收请求,调用 Service。
import{Controller,Get,Query}from'@nestjs/common';import{FlightService}from'./flight.service';import{FlightQueryDto}from'./dto/flight-query.dto';@Controller('ota/flight')// 相当于 @RequestMapping("/ota/flight")exportclassFlightController{// 构造函数注入 Serviceconstructor(privatereadonlyflightService:FlightService){}@Get('list')// 相当于 @GetMapping("/list")asyncgetList(@Query()query:FlightQueryDto){returnawaitthis.flightService.getFlightList(query);}}Step 4: 在 Module 中注册(⚠️最容易忘的一步)
必须将新建的 Controller 和 Service 注册到对应的 Module 中,否则 IoC 容器无法识别。
import{Module}from'@nestjs/common';import{FlightController}from'./flight.controller';import{FlightService}from'./flight.service';@Module({controllers:[FlightController],providers:[FlightService],// 相当于放入 Spring 容器exports:[FlightService],// 如果其他模块需要用 FlightService,必须导出})exportclassFlightModule{}第五阶段:团队编码规范与避坑指南
结合本项目的规范要求,在编码时请严格遵守以下几点:
- 异步处理 (Async/Await):Node.js 是单线程的,所有涉及 I/O(查库、HTTP请求、读写文件)的方法都必须返回
Promise,并在调用时使用await。 - 导入规范:使用
import { xxx } from '...'的方式导入,将相似的导入放在类的最上面,严禁在类中直接使用xx.xxx.xxx方式引用。 - 判空规范:优先使用
ObjectUtils.isEmpty()进行非空判断,而不是直接使用== null。 - 异常处理:除了打印日志,必须抛出异常(不要吞掉异常),以便被全局异常过滤器处理。
- 代码整洁度:
- 优先使用卫语句 (Guard Clause / early return)减少嵌套。
- 单个方法内保证有效代码在15行内。
- 复杂问题拆解为更小模块,复杂的校验逻辑应从 Service 抽离到独立的 Validator 类中。
- 当有常量时,优先抽出来作为常量进行调用。
- 面向对象与可维护性:
- 按最小开发单元生成代码,每个类功能要单一。
- 避免重复代码,建议使用继承、多态等机制复用。
- 避免出现内部类。
- 性能规范:考虑代码执行效率(如:不要循环插入数据库,应使用批量插入)。
- 并发与状态一致性:高并发场景下,倾向使用“Redis分布式锁 + 数据库唯一索引 + 条件更新(CAS)”组合方案。
编程学习
技术分享
实战经验