{T}

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 AOPAspect、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 实现说明
AspectInterceptor、Guard、Pipe、Filter、Middleware切面功能模块
Joinpoint方法调用、异常抛出程序执行点
Pointcut装饰器、路由配置切面执行位置
Adviceintercept、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 全局注册或检查装饰器位置
管道验证无效未启用 ValidationPipemain.ts 中添加 app.useGlobalPipes(new ValidationPipe())
过滤器捕获不到异常过滤器注册顺序问题确保过滤器在最后注册,检查异常类型匹配
AOP 组件执行顺序混乱不了解执行顺序参考本文档的执行顺序图
性能下降拦截器中执行耗时操作避免在拦截器中执行数据库查询、HTTP 请求
无法注入服务未正确配置依赖注入在 Module 的 providers 中注册服务
类型错误Pipe 未正确转换类型启用 transform: true 选项

八、学习要点总结

核心要点

  1. AOP 本质:面向切面编程,对 OOP 的补充,解决横切关注点问题
  2. 核心思想:不破坏原有业务,横向扩展功能,集中管理通用逻辑
  3. NestJS AOP:中间件、管道、守卫、拦截器、过滤器
  4. 执行顺序:中间件 → 守卫 → 拦截器(前置)→ 管道 → 控制器 → 拦截器(后置)
  5. 应用场景:日志、异常、权限、性能监控、事务管理

行动建议

code
学习路径:
│
├──  第一阶段:理解概念(1-2 天)
│   ├── 理解 AOP 核心概念
│   ├── 对比 OOP 和 AOP 的区别
│   └── 理解切面、连接点、切点、通知
│
├──  第二阶段:实践练习(1 周)
│   ├── 实现统一日志拦截器
│   ├── 实现统一异常过滤器
│   ├── 实现权限守卫
│   └── 实现参数验证管道
│
└──  第三阶段:项目应用(持续)
    ├── 在实际项目中应用 AOP
    ├── 根据场景选择合适的 AOP 组件
    └── 性能优化和最佳实践

九、延伸学习资源

官方资源

推荐阅读

练习建议

  1. 练习 1:实现一个性能监控拦截器,统计所有接口的响应时间
  2. 练习 2:实现一个缓存拦截器,对 GET 请求进行缓存
  3. 练习 3:实现一个请求限流守卫,防止接口被频繁调用
  4. 练习 4:实现一个数据脱敏管道,自动隐藏敏感信息
  5. 练习 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 深入实践