AOP面向切面编程
AOP面向切面编程
一、什么是 AOP
1.1 AOP 概念
概念说明
AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,它是对 OOP(面向对象编程)的补充和完善。
核心思想
code
AOP 核心理解:
│
├── 不破坏原有业务功能
│ ├── 不修改原有代码
│ ├── 不侵入原有类结构
│ └── 保持原有逻辑完整性
│
├── 横向扩展功能
│ ├── 在业务逻辑之外添加功能
│ ├── 统一管理扩展功能
│ └── 所有业务系统平等对待
│
└── 切面概念
└── 一刀切:所有业务系统在同一平面上,插入通用功能1.2 切面的形象理解(课堂比喻)
土豆切面比喻
code
切土豆的过程:
│
├── 土豆块
├── 土豆片
├── 土豆丝
└── 土豆丁
每个切面都是独立的一刀,互不影响
同样,AOP 的切面就是对所有业务系统的"一刀切"切面的本质
- 把多个业务系统看作一个整体
- 在同一位置插入通用功能
- 所有业务系统在同一切面上是平等关系
1.3 AOP vs OOP 对比
OOP 的局限性
场景:多业务系统需要添加统一功能
code
业务系统结构:
│
├── 业务系统 A
│ └── 需要添加日志功能
├── 业务系统 B
│ └── 需要添加日志功能
├── 业务系统 C
│ └── 需要添加日志功能
└── 业务系统 D
└── 需要添加日志功能OOP 的处理方式
typescript
// OOP 方式:每个类都要添加日志方法
class BusinessSystemA {
doSomething() {
this.log('开始执行业务 A'); // 复制粘贴
// 业务逻辑
this.log('业务 A 执行完成'); // 复制粘贴
}
// 每个类都要写一遍
private log(message: string) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
class BusinessSystemB {
doSomething() {
this.log('开始执行业务 B'); // 又要复制粘贴
// 业务逻辑
this.log('业务 B 执行完成'); // 又要复制粘贴
}
// 又要写一遍
private log(message: string) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
// 问题:
// 1. 代码重复(Ctrl+C、Ctrl+V)
// 2. 维护困难(修改日志格式要改所有类)
// 3. 违反 DRY 原则(Don't Repeat Yourself)AOP 的处理方式
typescript
// AOP 方式:统一日志切面,自动应用到所有业务
// 业务类保持纯净,不需要关心日志
class BusinessSystemA {
doSomething() {
// 只关注业务逻辑
// 日志功能由切面自动添加
}
}
class BusinessSystemB {
doSomething() {
// 只关注业务逻辑
// 日志功能由切面自动添加
}
}
// 日志切面(统一管理)
@Catch()
export class LoggingAspect {
intercept(context: ExecutionContext, next: CallHandler) {
console.log(`[${new Date().toISOString()}] 开始执行`);
return next.handle();
}
}
// 优势:
// 1. 业务代码纯净
// 2. 日志逻辑集中管理
// 3. 易于维护和扩展1.4 AOP 核心特点
| 特点 | 说明 | 优势 |
|---|---|---|
| 非侵入性 | 不破坏原有业务功能 | 原有代码保持完整 |
| 集中管理 | 扩展功能统一管理 | 避免代码重复 |
| 横向扩展 | 横跨多个业务模块 | 统一添加功能 |
| 松耦合 | 切面与业务分离 | 降低代码耦合度 |
| 高复用 | 一个切面服务多个模块 | 提高代码复用率 |
二、AOP 应用场景
2.1 典型应用场景
code
AOP 典型应用场景:
│
├── 统一日志管理
│ ├── 请求日志记录
│ ├── 响应日志记录
│ ├── 性能监控日志
│ └── 异常日志记录
│
├── 统一错误处理
│ ├── 全局异常捕获
│ ├── 错误信息格式化
│ └── 错误日志记录
│
├── 权限控制
│ ├── 身份认证
│ ├── 权限校验
│ ├── 角色验证
│ └── 访问控制
│
├── ⏱ 性能监控
│ ├── 方法执行时间统计
│ ├── 慢查询监控
│ └── 性能报告生成
│
├── 事务管理
│ ├── 事务开启
│ ├── 事务提交
│ └── 事务回滚
│
└── 安全控制
├── 参数校验
├── SQL 注入防护
└── XSS 防护2.2 AOP 在不同框架中的实现
| 框架 | AOP 实现方式 | 核心概念 |
|---|---|---|
| Spring(Java) | AspectJ、Spring AOP | Aspect、Pointcut、Advice |
| NestJS(Node.js) | 中间件、管道、守卫、拦截器 | Middleware、Pipe、Guard、Interceptor |
| Angular | 拦截器、守卫 | HttpInterceptor、CanActivate |
| Express | 中间件 | Middleware |
三、AOP 在 NestJS 中的应用
3.1 NestJS 的 AOP 机制
NestJS AOP 核心组件
code
NestJS AOP 四大组件:
│
├── Middleware(中间件)
│ ├── 请求预处理
│ ├── 日志记录
│ └── 响应处理
│
├── Pipe(管道)
│ ├── 参数验证
│ ├── 数据转换
│ └── 数据清洗
│
├── Guard(守卫)
│ ├── 身份认证
│ ├── 权限校验
│ └── 角色验证
│
└── Interceptor(拦截器)
├── 请求拦截
├── 响应拦截
├── 异常映射
└── 日志记录3.2 请求处理流程
完整的请求生命周期
code
HTTP 请求处理流程:
│
├── 1⃣ Incoming Request(请求到达)
│
├── 2⃣ Middleware(中间件)
│ └── 全局中间件 → 模块中间件
│
├── 3⃣ Guard(守卫)
│ └── 全局守卫 → 控制器守卫 → 路由守卫
│
├── 4⃣ Interceptor (Before)(拦截器-前置)
│ └── 全局拦截器 → 控制器拦截器 → 路由拦截器
│
├── 5⃣ Pipe(管道)
│ └── 全局管道 → 控制器管道 → 路由管道 → 参数管道
│
├── 6⃣ Controller Method(控制器方法)
│ └── 业务逻辑处理
│
├── 7⃣ Service(服务层)
│ └── 业务逻辑 / 数据库操作
│
├── 8⃣ Interceptor (After)(拦截器-后置)
│ └── 响应数据转换、日志记录
│
├── 9⃣ Exception Filter(异常过滤器)
│ └── 异常捕获和格式化
│
└── Response(响应返回)3.3 实战案例一:统一日志拦截器
需求描述
- 记录所有请求的基本信息(方法、路径、参数)
- 记录响应数据
- 统计请求处理时间
完整实现代码
typescript
// src/common/interceptors/logging.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url, body, query, params } = request;
const now = Date.now();
// 请求日志
this.logger.log(`\n
========== 请求开始 ==========
方法: ${method}
路径: ${url}
查询参数: ${JSON.stringify(query)}
路由参数: ${JSON.stringify(params)}
请求体: ${JSON.stringify(body)}
时间: ${new Date().toISOString()}
==============================
`);
return next.handle().pipe(
tap((data) => {
const duration = Date.now() - now;
// 响应日志
this.logger.log(`\n
========== 响应完成 ==========
方法: ${method}
路径: ${url}
响应数据: ${JSON.stringify(data)}
耗时: ${duration}ms
时间: ${new Date().toISOString()}
==============================
`);
}),
);
}
}
// ========== 注册全局拦截器 ==========
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './common/interceptors/logging.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局注册日志拦截器
app.useGlobalInterceptors(new LoggingInterceptor());
await app.listen(3000);
}
bootstrap();效果展示
bash
# 控制台输出
[LoggingInterceptor]
========== 请求开始 ==========
方法: POST
路径: /api/v1/users
查询参数: {}
路由参数: {}
请求体: {"name":"张三","email":"zhangsan@example.com"}
时间: 2026-03-07T10:30:00.000Z
==============================
[LoggingInterceptor]
========== 响应完成 ==========
方法: POST
路径: /api/v1/users
响应数据: {"code":0,"data":{"id":1,"name":"张三"},"message":"创建成功"}
耗时: 15ms
时间: 2026-03-07T10:30:00.015Z
==============================3.4 实战案例二:统一异常过滤器
需求描述
- 捕获所有异常
- 统一异常响应格式
- 记录异常日志
完整实现代码
typescript
// src/common/filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(HttpExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = '服务器内部错误';
let error = 'Internal Server Error';
// 判断异常类型
if (exception instanceof HttpException) {
status = exception.getStatus();
const exceptionResponse = exception.getResponse();
if (typeof exceptionResponse === 'object') {
message = (exceptionResponse as any).message || message;
error = (exceptionResponse as any).error || error;
} else {
message = exceptionResponse;
}
} else if (exception instanceof Error) {
message = exception.message;
error = exception.name;
}
// 记录错误日志
this.logger.error(`\n
========== 异常信息 ==========
路径: ${request.url}
方法: ${request.method}
状态码: ${status}
错误: ${error}
消息: ${message}
堆栈: ${exception instanceof Error ? exception.stack : 'N/A'}
==============================
`);
// 统一返回格式
const errorResponse = {
code: status,
data: null,
message: message,
error: error,
timestamp: new Date().toISOString(),
path: request.url,
};
response.status(status).json(errorResponse);
}
}
// ========== 注册全局过滤器 ==========
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局注册异常过滤器
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();效果展示
typescript
// 控制器中抛出异常
@Get(':id')
async findOne(@Param('id') id: string) {
const user = await this.userService.findOne(+id);
if (!user) {
throw new NotFoundException('用户不存在'); // 自动被过滤器捕获
}
return user;
}
// 返回统一格式
{
"code": 404,
"data": null,
"message": "用户不存在",
"error": "Not Found",
"timestamp": "2026-03-07T10:30:00.000Z",
"path": "/api/v1/users/999"
}3.5 实战案例三:权限守卫
需求描述
- 验证用户 Token
- 检查用户权限
- 无权限返回 401/403
完整实现代码
typescript
// src/common/guards/auth.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
import { ROLES_KEY } from '../decorators/roles.decorator';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// 检查是否为公开接口
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException('未提供认证令牌');
}
// 验证 Token(简化示例,实际应使用 JWT 验证)
const user = this.validateToken(token);
if (!user) {
throw new UnauthorizedException('无效的认证令牌');
}
// 将用户信息附加到请求对象
request.user = user;
// 检查角色权限
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (requiredRoles && !requiredRoles.some(role => user.roles.includes(role))) {
throw new ForbiddenException('无权访问该资源');
}
return true;
}
private extractTokenFromHeader(request: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
private validateToken(token: string): any {
// 简化示例:实际应使用 JWT 验证
// 这里模拟返回用户信息
if (token === 'valid-token') {
return {
id: 1,
username: 'admin',
roles: ['admin', 'user'],
};
}
return null;
}
}
// ========== 自定义装饰器 ==========
// src/common/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// src/common/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// ========== 使用示例 ==========
// src/users/users.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../common/guards/auth.guard';
import { Public } from '../common/decorators/public.decorator';
import { Roles } from '../common/decorators/roles.decorator';
@Controller('users')
@UseGuards(AuthGuard) // 整个控制器应用守卫
export class UsersController {
@Get('public')
@Public() // 公开接口,无需认证
getPublicData() {
return { message: '这是公开数据' };
}
@Get('profile')
getProfile() {
// 需要认证
return { message: '用户资料' };
}
@Get('admin')
@Roles('admin') // 需要 admin 角色
getAdminData() {
return { message: '管理员数据' };
}
}
// ========== 全局注册守卫 ==========
// src/app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './common/guards/auth.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}3.6 实战案例四:参数验证管道
需求描述
- 验证请求参数
- 自动转换数据类型
- 返回友好的错误信息
完整实现代码
typescript
// ========== 安装依赖 ==========
// npm install class-validator class-transformer
// ========== DTO 定义 ==========
// src/users/dto/create-user.dto.ts
import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';
export class CreateUserDto {
@IsString({ message: '用户名必须是字符串' })
name: string;
@IsEmail({}, { message: '邮箱格式不正确' })
email: string;
@IsInt({ message: '年龄必须是整数' })
@Min(0, { message: '年龄不能小于 0' })
@Max(150, { message: '年龄不能大于 150' })
@Type(() => Number) // 自动转换为数字
age: number;
@IsOptional()
@IsString()
address?: string;
}
// ========== 全局启用验证 ==========
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局启用验证管道
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // 过滤掉未在 DTO 中定义的属性
forbidNonWhitelisted: true, // 如果有未定义属性,抛出错误
transform: true, // 自动转换类型
transformOptions: {
enableImplicitConversion: true,
},
exceptionFactory: (errors) => {
// 自定义错误信息格式
const messages = errors.map(error => ({
property: error.property,
constraints: error.constraints,
}));
return new BadRequestException({
code: 400,
message: '参数验证失败',
errors: messages,
});
},
}),
);
await app.listen(3000);
}
bootstrap();
// ========== 控制器使用 ==========
// src/users/users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
// 参数已自动验证和转换
// 如果验证失败,自动返回 400 错误
return this.usersService.create(createUserDto);
}
}效果展示
bash
# 请求示例(验证失败)
POST /api/v1/users
{
"name": "张三",
"email": "invalid-email", // 格式错误
"age": -5 // 年龄无效
}
# 响应
{
"code": 400,
"message": "参数验证失败",
"errors": [
{
"property": "email",
"constraints": {
"isEmail": "邮箱格式不正确"
}
},
{
"property": "age",
"constraints": {
"min": "年龄不能小于 0"
}
}
]
}四、AOP 核心概念详解
4.1 AOP 核心术语
code
AOP 核心概念:
│
├── Aspect(切面)
│ └── 横跨多个类的功能模块,如日志、权限
│
├── Joinpoint(连接点)
│ └── 程序执行的某个特定位置,如方法调用、异常抛出
│
├── Pointcut(切点)
│ └── 匹配连接点的表达式,决定切面在何处执行
│
├── Advice(通知)
│ ├── Before:前置通知
│ ├── After:后置通知
│ ├── Around:环绕通知
│ ├── AfterReturning:返回后通知
│ └── AfterThrowing:异常通知
│
└── Weaving(织入)
└── 将切面应用到目标对象并创建代理对象的过程4.2 NestJS AOP 组件映射
| AOP 概念 | NestJS 实现 | 说明 |
|---|---|---|
| Aspect | Interceptor、Guard、Pipe、Filter、Middleware | 切面功能模块 |
| Joinpoint | 方法调用、异常抛出 | 程序执行点 |
| Pointcut | 装饰器、路由配置 | 切面执行位置 |
| Advice | intercept、canActivate、transform 等方法 | 切面执行逻辑 |
| Weaving | 依赖注入、装饰器系统 | 自动织入 |
4.3 执行顺序详解
完整执行顺序示例
typescript
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 1. 全局中间件
app.use((req, res, next) => {
console.log('1. 全局中间件');
next();
});
// 2. 全局管道
app.useGlobalPipes(new GlobalPipe());
// 3. 全局守卫
app.useGlobalGuards(new GlobalGuard());
// 4. 全局拦截器
app.useGlobalInterceptors(new GlobalInterceptor());
// 5. 全局过滤器
app.useGlobalFilters(new GlobalFilter());
await app.listen(3000);
}
bootstrap();
// 执行顺序(请求正常流程):
// 1. 全局中间件
// 2. 全局守卫
// 3. 全局拦截器 (前置)
// 4. 全局管道
// 5. 控制器方法
// 6. 全局拦截器 (后置)
// 执行顺序(异常情况):
// 1-4. 同上
// 5. 控制器方法抛出异常
// 6. 全局过滤器捕获异常五、AOP 设计模式对比
5.1 AOP vs 装饰器模式
typescript
// ========== 装饰器模式 ==========
// 函数装饰器
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用方法: ${propertyKey}`);
const result = originalMethod.apply(this, args);
console.log(`方法执行完成: ${propertyKey}`);
return result;
};
return descriptor;
}
class UserService {
@Log
getUser(id: number) {
return { id, name: '张三' };
}
}
// ========== AOP(NestJS 拦截器)==========
@Injectable()
export class LogInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
console.log('请求开始');
return next.handle().pipe(
tap(() => console.log('请求结束'))
);
}
}
@Controller('users')
@UseInterceptors(LogInterceptor)
export class UserController {
@Get(':id')
getUser(@Param('id') id: string) {
return { id, name: '张三' };
}
}5.2 AOP vs 中间件模式
typescript
// ========== 中间件模式 ==========
// Express 中间件
app.use((req, res, next) => {
console.log('请求日志');
next();
});
// ========== AOP(NestJS)==========
// NestJS 中间件
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('请求日志');
next();
}
}
// 在模块中配置
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}六、最佳实践
6.1 AOP 使用原则
code
AOP 最佳实践原则:
│
├── 单一职责
│ ├── 一个切面只做一件事
│ ├── 日志切面只负责日志
│ └── 权限切面只负责权限验证
│
├── 合理使用
│ ├── 横切关注点才使用 AOP
│ ├── 核心业务逻辑不应使用 AOP
│ └── 性能敏感场景慎用
│
├── 命名规范
│ ├── 拦截器:xxx.interceptor.ts
│ ├── 守卫:xxx.guard.ts
│ ├── 过滤器:xxx.filter.ts
│ └── 管道:xxx.pipe.ts
│
└── 文档完善
├── 切面功能说明
├── 执行时机说明
└── 配置参数说明6.2 性能优化建议
typescript
// 避免:在拦截器中执行耗时操作
@Injectable()
export class BadInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
// 不要在拦截器中执行数据库查询
await this.userService.getAllUsers(); // 性能杀手
return next.handle();
}
}
// 推荐:拦截器只做轻量级操作
@Injectable()
export class GoodInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
// 只做轻量级操作
const now = Date.now();
return next.handle().pipe(
tap(() => console.log(`耗时: ${Date.now() - now}ms`))
);
}
}6.3 错误处理最佳实践
typescript
// 推荐的错误处理方式
@Injectable()
export class ErrorHandlingInterceptor implements NestInterceptor {
private readonly logger = new Logger(ErrorHandlingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler) {
return next.handle().pipe(
catchError(error => {
// 1. 记录错误日志
this.logger.error(error.message, error.stack);
// 2. 区分错误类型
if (error instanceof HttpException) {
// 已知异常,直接抛出
return throwError(() => error);
}
// 3. 未知异常,包装成统一格式
return throwError(() =>
new InternalServerErrorException('服务器内部错误')
);
}),
);
}
}七、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 拦截器执行两次 | 全局注册 + 控制器注册 | 检查 useGlobalInterceptors 和 @UseInterceptors 是否重复 |
| 守卫不生效 | 未全局注册或装饰器位置错误 | 使用 APP_GUARD 全局注册或检查装饰器位置 |
| 管道验证无效 | 未启用 ValidationPipe | 在 main.ts 中添加 app.useGlobalPipes(new ValidationPipe()) |
| 过滤器捕获不到异常 | 过滤器注册顺序问题 | 确保过滤器在最后注册,检查异常类型匹配 |
| AOP 组件执行顺序混乱 | 不了解执行顺序 | 参考本文档的执行顺序图 |
| 性能下降 | 拦截器中执行耗时操作 | 避免在拦截器中执行数据库查询、HTTP 请求 |
| 无法注入服务 | 未正确配置依赖注入 | 在 Module 的 providers 中注册服务 |
| 类型错误 | Pipe 未正确转换类型 | 启用 transform: true 选项 |
八、学习要点总结
核心要点
- AOP 本质:面向切面编程,对 OOP 的补充,解决横切关注点问题
- 核心思想:不破坏原有业务,横向扩展功能,集中管理通用逻辑
- NestJS AOP:中间件、管道、守卫、拦截器、过滤器
- 执行顺序:中间件 → 守卫 → 拦截器(前置)→ 管道 → 控制器 → 拦截器(后置)
- 应用场景:日志、异常、权限、性能监控、事务管理
行动建议
code
学习路径:
│
├── 第一阶段:理解概念(1-2 天)
│ ├── 理解 AOP 核心概念
│ ├── 对比 OOP 和 AOP 的区别
│ └── 理解切面、连接点、切点、通知
│
├── 第二阶段:实践练习(1 周)
│ ├── 实现统一日志拦截器
│ ├── 实现统一异常过滤器
│ ├── 实现权限守卫
│ └── 实现参数验证管道
│
└── 第三阶段:项目应用(持续)
├── 在实际项目中应用 AOP
├── 根据场景选择合适的 AOP 组件
└── 性能优化和最佳实践九、延伸学习资源
官方资源
推荐阅读
- AOP in Spring Framework
- 《AspectJ in Action》
- 《设计模式:可复用面向对象软件的基础》
练习建议
- 练习 1:实现一个性能监控拦截器,统计所有接口的响应时间
- 练习 2:实现一个缓存拦截器,对 GET 请求进行缓存
- 练习 3:实现一个请求限流守卫,防止接口被频繁调用
- 练习 4:实现一个数据脱敏管道,自动隐藏敏感信息
- 练习 5:对比 Spring AOP 和 NestJS AOP 的实现差异
延伸思考
code
思考题:
│
├── 为什么说 AOP 是 OOP 的补充?
├── 在什么场景下应该使用 AOP?
├── NestJS 的四大 AOP 组件如何选择?
├── 如何避免 AOP 带来的性能问题?
└── AOP 和装饰器模式有什么区别?附录:NestJS AOP 组件速查表
| 组件 | 装饰器 | 接口 | 主要用途 | 执行时机 |
|---|---|---|---|---|
| Middleware | - | NestMiddleware | 请求预处理、日志 | 请求最开始 |
| Guard | @UseGuards() | CanActivate | 权限验证、认证 | 路由处理前 |
| Interceptor | @UseInterceptors() | NestInterceptor | 日志、缓存、转换 | 路由前后 |
| Pipe | @UsePipes() | PipeTransform | 参数验证、转换 | 参数处理时 |
| Filter | @Catch() | ExceptionFilter | 异常处理 | 异常发生时 |
笔记整理完成时间:2026-03-07
下一章节预告:NestJS 深入实践