| 1 | /* |
| 2 | * @Author: nevin |
| 3 | * @Date: 2025-01-24 17:55:15 |
| 4 | * @LastEditTime: 2025-01-24 19:50:24 |
| 5 | * @LastEditors: nevin |
| 6 | * @Description: 容器 |
| 7 | */ |
| 8 | import { INJECT_METADATA_KEY } from './metadata'; |
| 9 | // import { AppDataSource } from '../../db'; |
| 10 | |
| 11 | // 创建一个容器类来管理依赖注入 |
| 12 | export class Container { |
| 13 | private static instance: Container; |
| 14 | private readonly providers = new Map<string, any>(); |
| 15 | private readonly providerClasses = new Map<string, any>(); |
| 16 | private readonly controllers = new Map<string, any>(); |
| 17 | // 用于检测循环依赖 |
| 18 | private readonly dependencyStack: string[] = []; |
| 19 | |
| 20 | private constructor() {} |
| 21 | |
| 22 | static getInstance(): Container { |
| 23 | if (!Container.instance) { |
| 24 | Container.instance = new Container(); |
| 25 | } |
| 26 | return Container.instance; |
| 27 | } |
| 28 | |
| 29 | // 注册 provider 类 |
| 30 | registerProvider(providerClass: any) { |
| 31 | const providerName = providerClass.name; |
| 32 | if (!this.providerClasses.has(providerName)) { |
| 33 | // 检查依赖关系 |
| 34 | this.checkCircularDependencies(providerClass); |
| 35 | this.providerClasses.set(providerName, providerClass); |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // 检查循环依赖 |
| 40 | private checkCircularDependencies( |
| 41 | targetClass: any, |
| 42 | visited = new Set<string>(), |
| 43 | ) { |
| 44 | const className = targetClass.name; |
| 45 | |
| 46 | if (visited.has(className)) { |
| 47 | const dependencyPath = [...visited, className].join(' -> '); |
| 48 | throw new Error(`Circular dependency detected: ${dependencyPath}`); |
| 49 | } |
| 50 | |
| 51 | visited.add(className); |
| 52 | |
| 53 | const injections = |
| 54 | Reflect.getMetadata(INJECT_METADATA_KEY, targetClass) || []; |
| 55 | for (const { serviceType } of injections) { |
| 56 | const dependencyClass = serviceType; |
| 57 | this.checkCircularDependencies(dependencyClass, new Set(visited)); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | // 获取或创建 provider 实例 |
| 62 | getProvider(name: string) { |
| 63 | if (this.dependencyStack.includes(name)) { |
| 64 | throw new Error( |
| 65 | `Circular dependency detected: ${[...this.dependencyStack, name].join( |
| 66 | ' -> ', |
| 67 | )}`, |
| 68 | ); |
| 69 | } |
| 70 | |
| 71 | if (!this.providers.has(name)) { |
| 72 | const providerClass = this.providerClasses.get(name); |
| 73 | if (!providerClass) { |
| 74 | throw new Error(`Provider ${name} not registered`); |
| 75 | } |
| 76 | |
| 77 | this.dependencyStack.push(name); |
| 78 | const instance = new providerClass(); |
| 79 | this.injectDependencies(instance, providerClass); |
| 80 | this.dependencyStack.pop(); |
| 81 | |
| 82 | this.providers.set(name, instance); |
| 83 | } |
| 84 | return this.providers.get(name); |
| 85 | } |
| 86 | |
| 87 | // 注入依赖 |
| 88 | private injectDependencies(instance: any, targetClass: any) { |
| 89 | const injections = |
| 90 | Reflect.getMetadata(INJECT_METADATA_KEY, targetClass) || []; |
| 91 | injections.forEach(({ propertyKey, serviceType }: any) => { |
| 92 | instance[propertyKey] = this.getProvider(serviceType.name); |
| 93 | }); |
| 94 | } |
| 95 | |
| 96 | // Controller 相关方法 |
| 97 | setController(name: string, controller: any) { |
| 98 | if (!this.controllers.has(name)) { |
| 99 | this.injectDependencies(controller, controller.constructor); |
| 100 | this.controllers.set(name, controller); |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | getController(name: string) { |
| 105 | return this.controllers.get(name); |
| 106 | } |
| 107 | |
| 108 | hasController(name: string) { |
| 109 | return this.controllers.has(name); |
| 110 | } |
| 111 | |
| 112 | getAllProviders() { |
| 113 | return this.providers; |
| 114 | } |
| 115 | |
| 116 | getAllControllers() { |
| 117 | return this.controllers; |
| 118 | } |
| 119 | |
| 120 | // 添加初始化方法 |
| 121 | async initialize() { |
| 122 | // 初始化所有已注册的 providers |
| 123 | for (const [name, providerClass] of this.providerClasses.entries()) { |
| 124 | if (!this.providers.has(name)) { |
| 125 | const instance = new providerClass(); |
| 126 | this.injectDependencies(instance, providerClass); |
| 127 | this.providers.set(name, instance); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | export const container = Container.getInstance(); |
| 134 |