@dangao/bun-server 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/lifecycle.md CHANGED
@@ -1,21 +1,29 @@
1
1
  # Lifecycle Hooks
2
2
 
3
- Bun Server supports lifecycle hooks that let providers participate in application startup and shutdown. Implement the appropriate interface on your injectable services.
3
+ Bun Server supports lifecycle hooks that let components (`@Injectable` / `@Controller`) participate from creation to destruction.
4
4
 
5
5
  ## Interfaces
6
6
 
7
7
  | Interface | Method | When Called |
8
8
  |-----------|--------|-------------|
9
+ | `ComponentClassBeforeCreate` | `static onBeforeCreate()` | Right before the component instance is created |
10
+ | `OnAfterCreate` | `onAfterCreate()` | Right after instance creation and post processors |
9
11
  | `OnModuleInit` | `onModuleInit()` | After all module providers are registered |
10
12
  | `OnModuleDestroy` | `onModuleDestroy()` | During shutdown (reverse order) |
13
+ | `OnBeforeDestroy` | `onBeforeDestroy()` | Before `onModuleDestroy` during shutdown (reverse order) |
14
+ | `OnAfterDestroy` | `onAfterDestroy()` | After `onModuleDestroy` during shutdown (reverse order) |
11
15
  | `OnApplicationBootstrap` | `onApplicationBootstrap()` | After all modules init, before server listens |
12
16
  | `OnApplicationShutdown` | `onApplicationShutdown(signal?)` | When graceful shutdown begins |
13
17
 
14
18
  ## Execution Order
15
19
 
20
+ **Creation (per component instance)**: `onBeforeCreate` (static) -> instantiate -> `onAfterCreate`
21
+
16
22
  **Startup**: `onModuleInit` (all modules) -> `onApplicationBootstrap` (all modules) -> server starts
17
23
 
18
- **Shutdown**: `onApplicationShutdown` (reverse order) -> `onModuleDestroy` (reverse order)
24
+ **Shutdown**: `onApplicationShutdown` (reverse order) -> `onBeforeDestroy` (reverse order) -> `onModuleDestroy` (reverse order) -> `onAfterDestroy` (reverse order)
25
+
26
+ For `Lifecycle.Scoped` components, destroy hooks are executed automatically at the end of each request context.
19
27
 
20
28
  ## Provider Deduplication
21
29
 
@@ -23,19 +31,41 @@ When the same provider instance is exported or registered by multiple tokens,
23
31
  `onModuleInit` now runs only once for that instance. This avoids duplicate
24
32
  initialization side effects in shared singleton objects.
25
33
 
26
- ## Example: DatabaseService with init/destroy
34
+ ## Example: Component hooks from create to destroy
27
35
 
28
36
  ```ts
29
37
  import {
30
38
  Injectable,
39
+ Controller,
40
+ GET,
41
+ Module,
31
42
  OnModuleInit,
32
43
  OnModuleDestroy,
44
+ type ComponentClassBeforeCreate,
45
+ OnAfterCreate,
46
+ OnBeforeDestroy,
47
+ OnAfterDestroy,
33
48
  } from '@dangao/bun-server';
34
49
 
35
50
  @Injectable()
36
- class DatabaseService implements OnModuleInit, OnModuleDestroy {
51
+ class DatabaseService
52
+ implements
53
+ OnAfterCreate,
54
+ OnModuleInit,
55
+ OnBeforeDestroy,
56
+ OnModuleDestroy,
57
+ OnAfterDestroy
58
+ {
37
59
  private connected = false;
38
60
 
61
+ public static onBeforeCreate(): void {
62
+ console.log('[DatabaseService] Before create');
63
+ }
64
+
65
+ public onAfterCreate(): void {
66
+ console.log('[DatabaseService] After create');
67
+ }
68
+
39
69
  public async onModuleInit(): Promise<void> {
40
70
  console.log('[DatabaseService] Connecting...');
41
71
  await this.connect();
@@ -48,6 +78,14 @@ class DatabaseService implements OnModuleInit, OnModuleDestroy {
48
78
  this.connected = false;
49
79
  }
50
80
 
81
+ public onBeforeDestroy(): void {
82
+ console.log('[DatabaseService] Before destroy');
83
+ }
84
+
85
+ public onAfterDestroy(): void {
86
+ console.log('[DatabaseService] After destroy');
87
+ }
88
+
51
89
  public isConnected(): boolean {
52
90
  return this.connected;
53
91
  }
@@ -60,6 +98,36 @@ class DatabaseService implements OnModuleInit, OnModuleDestroy {
60
98
  // Close DB connection
61
99
  }
62
100
  }
101
+
102
+ @Controller('/health')
103
+ class HealthController implements OnAfterCreate, OnBeforeDestroy, OnAfterDestroy {
104
+ public static onBeforeCreate(): void {
105
+ console.log('[HealthController] Before create');
106
+ }
107
+
108
+ public onAfterCreate(): void {
109
+ console.log('[HealthController] After create');
110
+ }
111
+
112
+ public onBeforeDestroy(): void {
113
+ console.log('[HealthController] Before destroy');
114
+ }
115
+
116
+ public onAfterDestroy(): void {
117
+ console.log('[HealthController] After destroy');
118
+ }
119
+
120
+ @GET('/')
121
+ public get(): object {
122
+ return { ok: true };
123
+ }
124
+ }
125
+
126
+ @Module({
127
+ controllers: [HealthController],
128
+ providers: [DatabaseService],
129
+ })
130
+ class AppModule {}
63
131
  ```
64
132
 
65
133
  ## Example: Application-level hooks
@@ -75,4 +143,6 @@ class AppService implements OnApplicationBootstrap, OnApplicationShutdown {
75
143
  console.log(`Shutting down (signal: ${signal ?? 'none'})`);
76
144
  }
77
145
  }
146
+
147
+ const _beforeCreateHook: ComponentClassBeforeCreate = DatabaseService;
78
148
  ```
@@ -1,24 +1,31 @@
1
1
  # 生命周期钩子
2
2
 
3
- Bun Server 提供四个生命周期接口,用于在模块和应用启动、关闭时执行初始化或清理逻辑。
3
+ Bun Server 支持组件级生命周期钩子,可让 `@Injectable` / `@Controller` 从创建前到销毁后执行自定义逻辑。
4
4
 
5
5
  ## 接口定义
6
6
 
7
- - **OnModuleInit**:`onModuleInit()`,在模块所有 providers 注册完成后调用
8
- - **OnModuleDestroy**:`onModuleDestroy()`,在应用关闭时调用(反向顺序)
7
+ - **ComponentClassBeforeCreate**:`static onBeforeCreate()`,组件实例创建前调用
8
+ - **OnAfterCreate**:`onAfterCreate()`,组件实例创建并完成后处理后调用
9
+ - **OnModuleInit**:`onModuleInit()`,在模块所有组件初始化阶段调用
9
10
  - **OnApplicationBootstrap**:`onApplicationBootstrap()`,在所有模块初始化完成后、服务器开始监听前调用
10
11
  - **OnApplicationShutdown**:`onApplicationShutdown(signal?)`,在优雅停机开始时调用
12
+ - **OnBeforeDestroy**:`onBeforeDestroy()`,在 `onModuleDestroy()` 前调用(反向顺序)
13
+ - **OnModuleDestroy**:`onModuleDestroy()`,在应用关闭时调用(反向顺序)
14
+ - **OnAfterDestroy**:`onAfterDestroy()`,在 `onModuleDestroy()` 后调用(反向顺序)
11
15
 
12
16
  ## 执行顺序
13
17
 
18
+ **创建阶段(每个组件实例)**:`onBeforeCreate`(静态)→ 实例化 → `onAfterCreate`
19
+
14
20
  **启动阶段**:`onModuleInit` → `onApplicationBootstrap`
15
21
 
16
- **关闭阶段**:`onApplicationShutdown` → `onModuleDestroy`(均为反向顺序,即后注册的先执行)
22
+ **关闭阶段**:`onApplicationShutdown` → `onBeforeDestroy` → `onModuleDestroy` → `onAfterDestroy`(均为反向顺序,即后注册的先执行)
17
23
 
18
- ## Provider 去重行为
24
+ 对于 `Lifecycle.Scoped` 组件,请求上下文结束时会自动触发其销毁钩子。
19
25
 
20
- 当同一个 provider 实例通过多个 token 重复注册/导出时,`onModuleInit`
21
- 现只会执行一次,避免共享单例出现重复初始化副作用。
26
+ ## 组件去重行为
27
+
28
+ 当同一个组件实例通过多个 token 重复注册/导出时,生命周期钩子只会执行一次,避免共享单例出现重复副作用。
22
29
 
23
30
  ## 示例:DatabaseService 的初始化和销毁
24
31
 
@@ -41,6 +48,14 @@ import type {
41
48
  class DatabaseService implements OnModuleInit, OnModuleDestroy {
42
49
  private connected = false;
43
50
 
51
+ public static onBeforeCreate(): void {
52
+ console.log('[DatabaseService] 创建前');
53
+ }
54
+
55
+ public onAfterCreate(): void {
56
+ console.log('[DatabaseService] 创建后');
57
+ }
58
+
44
59
  public async onModuleInit(): Promise<void> {
45
60
  console.log('[DatabaseService] 正在连接数据库...');
46
61
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -54,6 +69,14 @@ class DatabaseService implements OnModuleInit, OnModuleDestroy {
54
69
  console.log('[DatabaseService] 已断开');
55
70
  }
56
71
 
72
+ public onBeforeDestroy(): void {
73
+ console.log('[DatabaseService] 销毁前');
74
+ }
75
+
76
+ public onAfterDestroy(): void {
77
+ console.log('[DatabaseService] 销毁后');
78
+ }
79
+
57
80
  public isConnected(): boolean {
58
81
  return this.connected;
59
82
  }
@@ -72,6 +95,22 @@ class AppService implements OnApplicationBootstrap, OnApplicationShutdown {
72
95
 
73
96
  @Controller('/api')
74
97
  class AppController {
98
+ public static onBeforeCreate(): void {
99
+ console.log('[AppController] 创建前');
100
+ }
101
+
102
+ public onAfterCreate(): void {
103
+ console.log('[AppController] 创建后');
104
+ }
105
+
106
+ public onBeforeDestroy(): void {
107
+ console.log('[AppController] 销毁前');
108
+ }
109
+
110
+ public onAfterDestroy(): void {
111
+ console.log('[AppController] 销毁后');
112
+ }
113
+
75
114
  @GET('/status')
76
115
  public status(): object {
77
116
  return { status: 'running', timestamp: Date.now() };
@@ -89,4 +128,4 @@ app.registerModule(AppModule);
89
128
  await app.listen();
90
129
  ```
91
130
 
92
- 按 Ctrl+C 触发关闭时,将依次执行 `onApplicationShutdown` 和 `onModuleDestroy`。
131
+ 按 Ctrl+C 触发关闭时,将依次执行 `onApplicationShutdown`、`onBeforeDestroy`、`onModuleDestroy`、`onAfterDestroy`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangao/bun-server",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -324,6 +324,7 @@ export class Application {
324
324
  */
325
325
  private async handleRequest(context: Context): Promise<Response> {
326
326
  const logger = LoggerManager.getLogger();
327
+ const moduleRegistry = ModuleRegistry.getInstance();
327
328
  logger.debug('[Request] Incoming', {
328
329
  method: context.method,
329
330
  path: context.path,
@@ -332,34 +333,46 @@ export class Application {
332
333
 
333
334
  // 使用 AsyncLocalStorage 包裹请求处理,确保所有中间件和控制器都在请求上下文中执行
334
335
  return await contextStore.run(context, async () => {
335
- // 对于 POST、PUT、PATCH 请求,提前解析 body 并缓存
336
- // 这样可以确保 Request.body 流只读取一次
337
- if (['POST', 'PUT', 'PATCH'].includes(context.method)) {
338
- await context.getBody();
339
- }
336
+ try {
337
+ // 对于 POST、PUT、PATCH 请求,提前解析 body 并缓存
338
+ // 这样可以确保 Request.body 流只读取一次
339
+ if (['POST', 'PUT', 'PATCH'].includes(context.method)) {
340
+ await context.getBody();
341
+ }
340
342
 
341
- // 先通过路由解析出处理器信息,便于安全中间件等基于路由元数据做决策
342
- const registry = RouteRegistry.getInstance();
343
- const router = registry.getRouter();
343
+ // 先通过路由解析出处理器信息,便于安全中间件等基于路由元数据做决策
344
+ const registry = RouteRegistry.getInstance();
345
+ const router = registry.getRouter();
344
346
 
345
- // 预解析路由,仅设置上下文信息,不执行处理器
346
- await router.preHandle(context);
347
+ // 预解析路由,仅设置上下文信息,不执行处理器
348
+ await router.preHandle(context);
347
349
 
348
- // 再进入中间件管道,由中间件(如安全过滤器)根据 routeHandler 和 Auth 元数据做校验,
349
- // 最后再由路由真正执行控制器方法
350
- return await this.middlewarePipeline.run(context, async () => {
351
- const response = await router.handle(context);
352
- if (response) {
353
- return response;
354
- }
350
+ // 再进入中间件管道,由中间件(如安全过滤器)根据 routeHandler 和 Auth 元数据做校验,
351
+ // 最后再由路由真正执行控制器方法
352
+ return await this.middlewarePipeline.run(context, async () => {
353
+ const response = await router.handle(context);
354
+ if (response) {
355
+ return response;
356
+ }
355
357
 
356
- logger.debug('[Router] No route matched', {
357
- method: context.method,
358
- path: context.path,
358
+ logger.debug('[Router] No route matched', {
359
+ method: context.method,
360
+ path: context.path,
361
+ });
362
+ context.setStatus(404);
363
+ return context.createErrorResponse({ error: 'Not Found' });
359
364
  });
360
- context.setStatus(404);
361
- return context.createErrorResponse({ error: 'Not Found' });
362
- });
365
+ } finally {
366
+ try {
367
+ await moduleRegistry.disposeScopedInstances(context);
368
+ } catch (error) {
369
+ logger.warn('[Application] Failed to dispose scoped instances', {
370
+ path: context.path,
371
+ method: context.method,
372
+ errorMessage: error instanceof Error ? error.message : String(error),
373
+ });
374
+ }
375
+ }
363
376
  });
364
377
  }
365
378
 
@@ -14,6 +14,13 @@ import {
14
14
  import { LoggerManager } from "@dangao/logsmith";
15
15
  import type { Constructor } from "@/core/types";
16
16
  import { contextStore } from "../core/context-service";
17
+ import {
18
+ callComponentBeforeCreate,
19
+ callOnAfterCreate,
20
+ callOnBeforeDestroy,
21
+ callOnModuleDestroy,
22
+ callOnAfterDestroy,
23
+ } from "./lifecycle";
17
24
 
18
25
  /**
19
26
  * 依赖注入容器
@@ -365,6 +372,7 @@ export class Container {
365
372
  * @returns 实例
366
373
  */
367
374
  private instantiate<T>(constructor: Constructor<T>): T {
375
+ callComponentBeforeCreate(constructor);
368
376
  const plan = this.getDependencyPlan(constructor);
369
377
 
370
378
  let instance: T;
@@ -379,7 +387,9 @@ export class Container {
379
387
  }
380
388
 
381
389
  // 应用后处理器
382
- return this.applyPostProcessors(instance, constructor);
390
+ const processed = this.applyPostProcessors(instance, constructor);
391
+ callOnAfterCreate(processed);
392
+ return processed;
383
393
  }
384
394
 
385
395
  /**
@@ -409,6 +419,50 @@ export class Container {
409
419
  // scopedInstances 使用 WeakMap,当 Context 对象被 GC 时会自动清理
410
420
  }
411
421
 
422
+ /**
423
+ * 获取指定请求上下文下的 scoped 实例
424
+ * @param context - 请求上下文对象
425
+ * @returns 去重后的 scoped 实例列表
426
+ */
427
+ public getScopedInstances(context: object): unknown[] {
428
+ const scopedMap = this.scopedInstances.get(context);
429
+ if (!scopedMap || scopedMap.size === 0) {
430
+ return [];
431
+ }
432
+ const seen = new Set<unknown>();
433
+ const instances: unknown[] = [];
434
+ for (const instance of scopedMap.values()) {
435
+ if (!seen.has(instance)) {
436
+ seen.add(instance);
437
+ instances.push(instance);
438
+ }
439
+ }
440
+ return instances;
441
+ }
442
+
443
+ /**
444
+ * 清理指定请求上下文的 scoped 实例缓存
445
+ * @param context - 请求上下文对象
446
+ */
447
+ public clearScopedInstances(context: object): void {
448
+ this.scopedInstances.delete(context);
449
+ }
450
+
451
+ /**
452
+ * 触发指定请求上下文下 scoped 实例的销毁钩子并清理缓存
453
+ * @param context - 请求上下文对象
454
+ */
455
+ public async disposeScopedInstances(context: object): Promise<void> {
456
+ const instances = this.getScopedInstances(context);
457
+ if (instances.length === 0) {
458
+ return;
459
+ }
460
+ await callOnBeforeDestroy(instances);
461
+ await callOnModuleDestroy(instances);
462
+ await callOnAfterDestroy(instances);
463
+ this.clearScopedInstances(context);
464
+ }
465
+
412
466
  /**
413
467
  * 检查是否已注册
414
468
  * @param token - 提供者标识符
@@ -30,6 +30,38 @@ export interface OnApplicationShutdown {
30
30
  onApplicationShutdown(signal?: string): Promise<void> | void;
31
31
  }
32
32
 
33
+ /**
34
+ * 组件创建前钩子(静态类方法)
35
+ * 在实例化前调用,适用于 Controller / Injectable 类
36
+ */
37
+ export type ComponentClassBeforeCreate = {
38
+ onBeforeCreate(): void;
39
+ };
40
+
41
+ /**
42
+ * 组件创建后钩子(实例)
43
+ * 在实例化并完成后处理后调用
44
+ */
45
+ export interface OnAfterCreate {
46
+ onAfterCreate(): void;
47
+ }
48
+
49
+ /**
50
+ * 组件销毁前钩子(实例)
51
+ * 在 onModuleDestroy 之前调用(反向顺序)
52
+ */
53
+ export interface OnBeforeDestroy {
54
+ onBeforeDestroy(): Promise<void> | void;
55
+ }
56
+
57
+ /**
58
+ * 组件销毁后钩子(实例)
59
+ * 在 onModuleDestroy 之后调用(反向顺序)
60
+ */
61
+ export interface OnAfterDestroy {
62
+ onAfterDestroy(): Promise<void> | void;
63
+ }
64
+
33
65
  export function hasOnModuleInit(instance: unknown): instance is OnModuleInit {
34
66
  return (
35
67
  instance !== null &&
@@ -70,6 +102,64 @@ export function hasOnApplicationShutdown(instance: unknown): instance is OnAppli
70
102
  );
71
103
  }
72
104
 
105
+ export function hasComponentBeforeCreate(target: unknown): target is ComponentClassBeforeCreate {
106
+ return (
107
+ target !== null &&
108
+ target !== undefined &&
109
+ typeof target === 'function' &&
110
+ 'onBeforeCreate' in target &&
111
+ typeof (target as ComponentClassBeforeCreate).onBeforeCreate === 'function'
112
+ );
113
+ }
114
+
115
+ export function hasOnAfterCreate(instance: unknown): instance is OnAfterCreate {
116
+ return (
117
+ instance !== null &&
118
+ instance !== undefined &&
119
+ typeof instance === 'object' &&
120
+ 'onAfterCreate' in instance &&
121
+ typeof (instance as OnAfterCreate).onAfterCreate === 'function'
122
+ );
123
+ }
124
+
125
+ export function hasOnBeforeDestroy(instance: unknown): instance is OnBeforeDestroy {
126
+ return (
127
+ instance !== null &&
128
+ instance !== undefined &&
129
+ typeof instance === 'object' &&
130
+ 'onBeforeDestroy' in instance &&
131
+ typeof (instance as OnBeforeDestroy).onBeforeDestroy === 'function'
132
+ );
133
+ }
134
+
135
+ export function hasOnAfterDestroy(instance: unknown): instance is OnAfterDestroy {
136
+ return (
137
+ instance !== null &&
138
+ instance !== undefined &&
139
+ typeof instance === 'object' &&
140
+ 'onAfterDestroy' in instance &&
141
+ typeof (instance as OnAfterDestroy).onAfterDestroy === 'function'
142
+ );
143
+ }
144
+
145
+ /**
146
+ * 调用组件类静态 onBeforeCreate
147
+ */
148
+ export function callComponentBeforeCreate(target: unknown): void {
149
+ if (hasComponentBeforeCreate(target)) {
150
+ target.onBeforeCreate();
151
+ }
152
+ }
153
+
154
+ /**
155
+ * 调用组件实例 onAfterCreate
156
+ */
157
+ export function callOnAfterCreate(instance: unknown): void {
158
+ if (hasOnAfterCreate(instance)) {
159
+ instance.onAfterCreate();
160
+ }
161
+ }
162
+
73
163
  /**
74
164
  * 按顺序调用 onModuleInit
75
165
  */
@@ -115,3 +205,27 @@ export async function callOnApplicationShutdown(instances: unknown[], signal?: s
115
205
  }
116
206
  }
117
207
  }
208
+
209
+ /**
210
+ * 按反向顺序调用 onBeforeDestroy
211
+ */
212
+ export async function callOnBeforeDestroy(instances: unknown[]): Promise<void> {
213
+ for (let i = instances.length - 1; i >= 0; i--) {
214
+ const instance = instances[i];
215
+ if (hasOnBeforeDestroy(instance)) {
216
+ await instance.onBeforeDestroy();
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * 按反向顺序调用 onAfterDestroy
223
+ */
224
+ export async function callOnAfterDestroy(instances: unknown[]): Promise<void> {
225
+ for (let i = instances.length - 1; i >= 0; i--) {
226
+ const instance = instances[i];
227
+ if (hasOnAfterDestroy(instance)) {
228
+ await instance.onAfterDestroy();
229
+ }
230
+ }
231
+ }
@@ -11,6 +11,8 @@ import {
11
11
  callOnModuleDestroy,
12
12
  callOnApplicationBootstrap,
13
13
  callOnApplicationShutdown,
14
+ callOnBeforeDestroy,
15
+ callOnAfterDestroy,
14
16
  } from './lifecycle';
15
17
 
16
18
  interface ModuleRef {
@@ -225,9 +227,9 @@ export class ModuleRegistry {
225
227
  }
226
228
 
227
229
  /**
228
- * 解析所有模块中的 provider 实例,用于生命周期钩子调用
230
+ * 解析所有模块中的组件实例(providers + controllers),用于生命周期钩子调用
229
231
  */
230
- public resolveAllProviderInstances(): unknown[] {
232
+ public resolveAllComponentInstances(): unknown[] {
231
233
  const instances: unknown[] = [];
232
234
  const seen = new Set<unknown>();
233
235
  for (const [, ref] of this.moduleRefs) {
@@ -262,42 +264,88 @@ export class ModuleRegistry {
262
264
  // skip providers that can't be resolved (e.g. pending async providers)
263
265
  }
264
266
  }
267
+ for (const controller of ref.metadata.controllers) {
268
+ try {
269
+ const instance = ref.container.resolve(controller);
270
+ if (!seen.has(instance)) {
271
+ seen.add(instance);
272
+ instances.push(instance);
273
+ }
274
+ } catch (_error) {
275
+ // skip controllers that can't be resolved
276
+ }
277
+ }
265
278
  }
266
279
  return instances;
267
280
  }
268
281
 
269
282
  /**
270
- * 调用所有 provider onModuleInit 钩子
283
+ * 调用所有组件(providers + controllers)的 onModuleInit 钩子
271
284
  */
272
285
  public async callModuleInitHooks(): Promise<void> {
273
- const instances = this.resolveAllProviderInstances();
286
+ const instances = this.resolveAllComponentInstances();
274
287
  await callOnModuleInit(instances);
275
288
  }
276
289
 
277
290
  /**
278
- * 调用所有 provider onApplicationBootstrap 钩子
291
+ * 调用所有组件(providers + controllers)的 onApplicationBootstrap 钩子
279
292
  */
280
293
  public async callBootstrapHooks(): Promise<void> {
281
- const instances = this.resolveAllProviderInstances();
294
+ const instances = this.resolveAllComponentInstances();
282
295
  await callOnApplicationBootstrap(instances);
283
296
  }
284
297
 
285
298
  /**
286
- * 调用所有 provider onModuleDestroy 钩子
299
+ * 调用所有组件(providers + controllers)的 onModuleDestroy 钩子
287
300
  */
288
301
  public async callModuleDestroyHooks(): Promise<void> {
289
- const instances = this.resolveAllProviderInstances();
302
+ const instances = this.resolveAllComponentInstances();
303
+ await callOnBeforeDestroy(instances);
290
304
  await callOnModuleDestroy(instances);
305
+ await callOnAfterDestroy(instances);
291
306
  }
292
307
 
293
308
  /**
294
- * 调用所有 provider onApplicationShutdown 钩子
309
+ * 调用所有组件(providers + controllers)的 onApplicationShutdown 钩子
295
310
  */
296
311
  public async callShutdownHooks(signal?: string): Promise<void> {
297
- const instances = this.resolveAllProviderInstances();
312
+ const instances = this.resolveAllComponentInstances();
298
313
  await callOnApplicationShutdown(instances, signal);
299
314
  }
300
315
 
316
+ /**
317
+ * 调用当前请求上下文下 scoped 组件的销毁钩子并清理缓存
318
+ */
319
+ public async disposeScopedInstances(context: object): Promise<void> {
320
+ const containers: Container[] = [];
321
+ if (this.rootContainer) {
322
+ containers.push(this.rootContainer);
323
+ }
324
+ containers.push(...this.getAllModuleContainers());
325
+
326
+ const instances: unknown[] = [];
327
+ const seen = new Set<unknown>();
328
+ for (const container of containers) {
329
+ const scopedInstances = container.getScopedInstances(context);
330
+ for (const instance of scopedInstances) {
331
+ if (!seen.has(instance)) {
332
+ seen.add(instance);
333
+ instances.push(instance);
334
+ }
335
+ }
336
+ }
337
+
338
+ if (instances.length > 0) {
339
+ await callOnBeforeDestroy(instances);
340
+ await callOnModuleDestroy(instances);
341
+ await callOnAfterDestroy(instances);
342
+ }
343
+
344
+ for (const container of containers) {
345
+ container.clearScopedInstances(context);
346
+ }
347
+ }
348
+
301
349
  private registerExport(parentContainer: Container, moduleRef: ModuleRef, token: ProviderToken): void {
302
350
  if (!moduleRef.container.isRegistered(token)) {
303
351
  throw new Error(
package/src/index.ts CHANGED
@@ -42,6 +42,10 @@ export type {
42
42
  OnModuleDestroy,
43
43
  OnApplicationBootstrap,
44
44
  OnApplicationShutdown,
45
+ ComponentClassBeforeCreate,
46
+ OnAfterCreate,
47
+ OnBeforeDestroy,
48
+ OnAfterDestroy,
45
49
  } from './di/lifecycle';
46
50
  export {
47
51
  InterceptorRegistry,