跳到主要内容

装饰器

Tarpit 使用 TypeScript 装饰器创建强大的分层组件系统。理解装饰器架构是掌握 Tarpit 依赖注入和组件组织的关键。

装饰器架构概述

Tarpit 的装饰器系统遵循清晰的继承层次结构:

TpComponent (所有 Tp 装饰器的基类)

├── TpWorker (功能单元 - 可被注入)
│ │
│ └── @TpService (可注入服务)

├── TpAssembly (模块组装单元 - 具有导入/提供者)
│ │
│ ├── @TpModule (依赖组织模块)
│ │
│ └── TpEntry (入口点服务 - 依赖树端点)
│ │
│ └── @TpRoot (注入层次边界 - 创建子注入器)

└── TpUnit (特殊方法标记)

├── @OnStart (生命周期初始化)

└── @OnTerminate (清理操作)

核心组件类型

🔧 TpWorker - 功能单元

  • 可以作为依赖项注入
  • 应用程序逻辑的构建块
  • 默认为单例

示例:@TpService()

📦 TpAssembly - 模块组织

  • 具有导入/提供者的模块组装
  • 控制服务暴露
  • 分组相关功能

示例:@TpModule()

⚡ TpEntry - 入口点服务

  • 加载时始终实例化
  • 依赖树端点
  • 特定功能的功能入口点

示例:@TpRouter()@TpConsumer()

🚪 TpRoot - 注入边界

  • 创建子注入器
  • 模块隔离边界
  • 分层依赖管理

示例:@TpRoot()

⚙️ TpUnit - 方法标记

  • 特殊方法行为
  • 生命周期管理
  • 功能扩展

示例:@OnStart@OnTerminate

关键优势

  • 清晰的关注点分离 - 每种装饰器类型都有特定的作用
  • 可预测的行为 - 子装饰器继承父级能力
  • 灵活的组合 - 装饰器可以有效组合
  • 可扩展的架构 - 随着应用程序增长易于扩展和维护

抽象装饰器

以下是构成 Tarpit 装饰器系统基础的抽象基类。这些不直接使用,但为具体装饰器提供继承层次结构。

TpComponent

TpComponent 是所有 Tarpit 类装饰器的基础。它提供:

  • 通过标记进行唯一标识
  • 实例管理功能
  • DI 系统的集成

所有 Tarpit 类装饰器都继承自 TpComponent,确保整个框架的行为一致性。

TpWorker - 功能单元

TpWorker 扩展了 TpComponent,用于表示可以作为依赖项注入的功能单元。Worker 是应用程序逻辑的构建块。

TpAssembly - 模块组织

TpAssembly 扩展了 TpComponent 以支持模块组装,具有:

  • imports - 导入其他模块的能力
  • providers - 模块提供的服务声明

TpEntry - 入口点服务

TpEntry 扩展了 TpAssembly,用于标记在依赖树中作为功能端点的入口点服务。这些组件在加载时始终实例化:

  • 始终实例化 - 无论依赖使用情况如何都会创建实例
  • 依赖树端点 - 位于依赖树的叶节点
  • 功能入口点 - 作为特定应用程序功能(如路由、消息消费等)的入口点

TpRoot - 注入边界

为什么 TpRoot 出现在这里

虽然 TpRoot 是一个具体的装饰器(不是抽象的),但它出现在此部分是因为它在装饰器层次结构中起着基础架构作用。作为唯一创建子注入器的装饰器,在学习其实际使用模式之前,理解 TpRoot 的架构行为是至关重要的。

TpRoot 扩展了 TpEntry 以创建注入层次边界。当平台遇到 TpRoot 时,它会创建一个子注入器,使得:

  • 模块隔离 - 不同模块可以有单独的依赖实现
  • 作用域服务 - 限制在特定应用程序部分的服务
  • 分层结构 - 跨应用程序层的有组织的依赖管理

TpUnit - 方法标记

TpUnit 用于标记具有特定行为的特殊方法,特别是用于生命周期管理和功能扩展。

工作组件

@TpService(扩展 TpWorker)

@TpService() 装饰器将类标记为可注入服务:

import { TpService } from '@tarpit/core'

@TpService()
class UserService {
private users: User[] = []

create_user(name: string): User {
const user = { id: Date.now(), name }
this.users.push(user)
return user
}

find_user(id: number): User | undefined {
return this.users.find(u => u.id === id)
}
}

关键特征:

  • 可注入 - 可以通过构造函数注入到其他服务中
  • 默认单例 - 每个注入器作用域一个实例
  • 依赖解析 - 依赖项自动注入

服务选项

@TpService({
inject_root: true, // 从根注入器而不是当前注入器注入
echo_dependencies: true // 在初始化期间记录依赖信息
})
class UserRepository {
// 此服务将从根注入器注入
// 并在创建时输出依赖信息
}

@TpModule(扩展 TpAssembly)

@TpModule() 装饰器分组相关服务并管理依赖项:

import { TpModule, TpService } from '@tarpit/core'

@TpService()
class UserService { /* ... */ }

@TpService()
class UserRepository { /* ... */ }

@TpModule({
providers: [UserService, UserRepository], // 此模块提供的服务
imports: [DatabaseModule], // 要导入的其他模块
inject_root: true // 可选:从根注入器注入
})
class UserModule {}

模块功能:

  • 提供者注册 - 声明模块提供哪些服务
  • 模块组合 - 从其他模块导入功能
  • 根注入 - 可选择从根注入器而不是当前作用域注入
  • 依赖组织 - 分组相关功能
模块导出

与某些 DI 系统不同,Tarpit 模块不使用显式的 exports。模块中声明的所有提供者都自动可用于导入它的模块。这简化了模块组织并减少了配置开销。

@TpRoot(扩展 TpEntry)

@TpRoot() 装饰器定义应用程序入口点并创建隔离的依赖作用域:

import { TpRoot } from '@tarpit/core'

@TpRoot({
imports: [UserModule, DatabaseModule], // 要导入的模块
entries: [UserRouter, TaskScheduler], // 要实例化的入口点服务
providers: [GlobalService] // 此作用域的额外服务
})
class AppRoot {}

// 使用导入启动应用程序
const platform = new Platform(config)
.import(AppRoot)

await platform.start()

实际用途:

  • 应用程序引导 - 为你的应用程序定义主入口点
  • 模块隔离 - 为应用程序的不同部分创建单独的上下文(Web 服务器、后台作业、测试)
  • 入口点管理 - 自动实例化和管理入口点服务,如路由器和消费者
  • 配置作用域 - 为不同环境提供不同的服务实现
TpRoot 和 inject_root

@TpRoot 不支持 inject_root 选项,因为它与 TpRoot 创建注入边界的核心目的冲突。TpRoot 实例始终在它们自己的子注入器上下文中创建。

参数装饰器

@Inject

使用 @Inject() 指定自定义注入标记:

import { Inject, TpService } from '@tarpit/core'

// 定义标记
const DATABASE_URL = Symbol('DATABASE_URL')
const MAX_RETRIES = Symbol('MAX_RETRIES')

@TpService()
class DatabaseService {
constructor(
@Inject(DATABASE_URL) private url: string,
@Inject(MAX_RETRIES) private max_retries: number
) {}

connect() {
console.log(`Connecting to: ${this.url} (max retries: ${this.max_retries})`)
}
}

// 注册值
platform
.import({ provide: DATABASE_URL, useValue: 'postgresql://localhost:5432' })
.import({ provide: MAX_RETRIES, useValue: 3 })
.import(DatabaseService)

@Optional

将依赖项标记为可选:

import { Optional, TpService } from '@tarpit/core'

@TpService()
class EmailService {
constructor(
private logger: LoggerService,
@Optional() private metrics?: MetricsService // 可选依赖项
) {}

send_email(to: string, subject: string) {
this.logger.log(`Sending email to ${to}`)

// 安全使用 - 可能是 undefined
this.metrics?.increment('emails_sent')
}
}

@Disabled

标记类、方法或参数被特定模块跳过:

import { Disabled, TpService } from '@tarpit/core'

@TpService()
class FileService {
constructor(
private logger: LoggerService,
@Disabled() private base_dir: string = '/tmp' // 标记为被 DI 系统跳过
) {}

@Disabled()
deprecated_method() {
// 此方法可能被某些处理模块跳过
}
}

@Disabled()
@TpService()
class LegacyService {
// 整个服务可能被某些模块跳过
}

关键特征:

  • 标记装饰器 - 只是添加元数据,不直接改变行为
  • 特定于模块的行为 - 如何处理取决于消费模块
  • 一般含义 - 表示在处理过程中应该跳过装饰的元素

方法装饰器(基于 TpUnit)

@OnStart

标记在创建服务时要调用的方法:

import { TpService, OnStart } from '@tarpit/core'

@TpService()
class DatabaseService {
private connection: any

@OnStart()
async initialize() {
console.log('DatabaseService: Connecting to database...')
this.connection = await this.create_connection()
console.log('DatabaseService: Connected successfully')
}

private async create_connection() {
// 数据库连接逻辑
}
}

@OnTerminate

标记在应用程序关闭期间要调用的方法:

import { TpService, OnTerminate } from '@tarpit/core'

@TpService()
class DatabaseService {
private connection: any

@OnTerminate()
async cleanup() {
console.log('DatabaseService: Closing connection...')
if (this.connection) {
await this.connection.close()
}
console.log('DatabaseService: Connection closed')
}
}

高级模式

分层模块组织

利用装饰器层次结构实现清洁架构:

// 低层服务(TpWorker)
@TpService()
class DatabaseService { /* ... */ }

@TpService()
class UserRepository { /* ... */ }

// 中层模块组织(TpAssembly)
@TpModule({
providers: [DatabaseService, UserRepository]
})
class DatabaseModule {}

@TpModule({
imports: [DatabaseModule],
providers: [UserService]
})
class UserModule {}

// 具有入口点服务的注入边界(TpRoot 扩展 TpEntry)
@TpRoot({
imports: [UserModule],
entries: [UserController] // UserController 是入口点服务 - 始终实例化
})
class AppRoot {}

具有多个装饰器的复杂服务

@TpService({ inject_root: true })
class AdvancedService {
constructor(
private logger: LoggerService,
@Optional() @Inject('feature-flag') private feature_enabled?: boolean,
@Disabled() private debug_mode: boolean = false
) {}

@OnStart()
async initialize() {
this.logger.log('AdvancedService initializing...')
if (this.feature_enabled) {
await this.enable_advanced_features()
}
}

@OnTerminate()
async cleanup() {
this.logger.log('AdvancedService shutting down...')
await this.perform_cleanup()
}

private async enable_advanced_features() {
// 功能初始化
}

private async perform_cleanup() {
// 清理逻辑
}
}

注入边界管理

使用 TpRoot 装饰器创建隔离的注入器作用域:

// 全局作用域
@TpService()
class GlobalConfigService { /* ... */ }

// HTTP 请求作用域 - 单独的注入器
@TpRoot({
imports: [UserModule],
providers: [RequestContextService], // 作用域限于此注入器
entries: [HttpController] // HTTP 处理的入口点服务
})
class HttpRequestHandler {}

// 后台作业作用域 - 单独的注入器
@TpRoot({
imports: [UserModule],
providers: [JobContextService], // 与 HTTP 作用域不同的实例
entries: [JobProcessor] // 作业处理的入口点服务
})
class BackgroundJobHandler {}