从 Spring Boot 到 NestJS:项目快速上手指南
·
从 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")
export class User {
@PrimaryGeneratedColumn() // @Id @GeneratedValue
id: number;
@Column({ name: 'user_name', length: 50 }) // @Column
userName: 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()
export class UserService {
// 等同于 @Autowired private UserRepository userRepository;
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async findUser(id: number) {
return await this.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';
export class FlightQueryDto {
@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()
export class FlightService {
async getFlightList(query: FlightQueryDto) {
// 卫语句:优先拦截异常情况
if (ObjectUtils.isEmpty(query.depCity) || ObjectUtils.isEmpty(query.arrCity)) {
throw new Error('城市参数缺失'); // 全局 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")
export class FlightController {
// 构造函数注入 Service
constructor(private readonly flightService: FlightService) {}
@Get('list') // 相当于 @GetMapping("/list")
async getList(@Query() query: FlightQueryDto) {
return await this.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,必须导出
})
export class FlightModule {}
第五阶段:团队编码规范与避坑指南
结合本项目的规范要求,在编码时请严格遵守以下几点:
- 异步处理 (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)”组合方案。
更多推荐



所有评论(0)