203 lines
5.8 KiB
TypeScript
203 lines
5.8 KiB
TypeScript
|
|
/**
|
|||
|
|
* 依赖注入装饰器
|
|||
|
|
* 提供类似Java的自动注入功能
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { ServiceProvider } from './ServiceProvider'
|
|||
|
|
import { ServiceRegistry } from './ServiceRegistry'
|
|||
|
|
import 'reflect-metadata';
|
|||
|
|
|
|||
|
|
// 定义构造函数类型
|
|||
|
|
type Constructor<T = any> = new (...args: any[]) => T
|
|||
|
|
|
|||
|
|
// 存储类到服务ID的映射
|
|||
|
|
const classToServiceIdMap = new Map<Constructor, string>()
|
|||
|
|
// 存储服务ID到类的映射
|
|||
|
|
const serviceIdToClassMap = new Map<string, Constructor>()
|
|||
|
|
// 存储需要延迟注册的服务
|
|||
|
|
const pendingServices: Array<{
|
|||
|
|
serviceClass: Constructor
|
|||
|
|
serviceId: string
|
|||
|
|
dependencies: Array<string | Constructor>
|
|||
|
|
}> = []
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 服务装饰器
|
|||
|
|
* 用于标记一个类作为可注入的服务
|
|||
|
|
* @param options 服务配置选项
|
|||
|
|
*/
|
|||
|
|
export function Service(options?: {
|
|||
|
|
/** 自定义服务ID,如果不提供则使用类名 */
|
|||
|
|
id?: string
|
|||
|
|
/** 服务依赖,可以是服务ID字符串或依赖类 */
|
|||
|
|
dependencies?: Array<string | Constructor>
|
|||
|
|
/** 是否为单例模式,默认为true */
|
|||
|
|
singleton?: boolean
|
|||
|
|
}): ClassDecorator {
|
|||
|
|
return (target: Function) => {
|
|||
|
|
// 断言target为Constructor类型
|
|||
|
|
const serviceClass = target as Constructor
|
|||
|
|
const serviceId = options?.id || target.name
|
|||
|
|
const dependencies = options?.dependencies || []
|
|||
|
|
|
|||
|
|
// 保存映射关系
|
|||
|
|
classToServiceIdMap.set(serviceClass, serviceId)
|
|||
|
|
serviceIdToClassMap.set(serviceId, serviceClass)
|
|||
|
|
|
|||
|
|
// 将服务添加到待注册列表
|
|||
|
|
pendingServices.push({
|
|||
|
|
serviceClass,
|
|||
|
|
serviceId,
|
|||
|
|
dependencies
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 尝试注册服务
|
|||
|
|
tryRegisterPendingServices()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 注入装饰器
|
|||
|
|
* 用于自动注入服务实例
|
|||
|
|
* @param dependency 依赖的服务类或服务ID
|
|||
|
|
*/
|
|||
|
|
export function Inject(dependency?: Function | string): PropertyDecorator {
|
|||
|
|
return (target: Object, propertyKey: string | symbol) => {
|
|||
|
|
// 获取属性的类型(如果使用了TypeScript)
|
|||
|
|
const propertyType = Reflect.getMetadata('design:type', target, propertyKey)
|
|||
|
|
|
|||
|
|
const descriptor = {
|
|||
|
|
get: () => {
|
|||
|
|
try {
|
|||
|
|
let serviceId: string
|
|||
|
|
|
|||
|
|
if (typeof dependency === 'string') {
|
|||
|
|
// 直接使用提供的服务ID
|
|||
|
|
serviceId = dependency
|
|||
|
|
} else if (dependency) {
|
|||
|
|
// 使用提供的类获取服务ID
|
|||
|
|
serviceId = getServiceIdForClass(dependency)
|
|||
|
|
} else if (propertyType) {
|
|||
|
|
// 使用属性类型获取服务ID
|
|||
|
|
serviceId = getServiceIdForClass(propertyType)
|
|||
|
|
} else {
|
|||
|
|
throw new Error(`无法确定属性 ${String(propertyKey)} 的注入类型`)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ServiceProvider.getService(serviceId)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`注入服务失败: ${String(propertyKey)}`, error)
|
|||
|
|
throw error
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Object.defineProperty(target, propertyKey, descriptor)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取类对应的服务ID
|
|||
|
|
* @param cls 服务类
|
|||
|
|
*/
|
|||
|
|
function getServiceIdForClass(cls: Function): string {
|
|||
|
|
const serviceId = classToServiceIdMap.get(cls)
|
|||
|
|
if (!serviceId) {
|
|||
|
|
throw new Error(`类 ${cls.name} 未注册为服务`)
|
|||
|
|
}
|
|||
|
|
return serviceId
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 尝试注册所有待注册的服务
|
|||
|
|
*/
|
|||
|
|
function tryRegisterPendingServices(): void {
|
|||
|
|
if (pendingServices.length === 0) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const registry = ServiceRegistry.getInstance()
|
|||
|
|
const container = registry.getContainer()
|
|||
|
|
|
|||
|
|
// 处理所有待注册的服务
|
|||
|
|
for (const pendingService of pendingServices.slice()) {
|
|||
|
|
// 解析依赖的服务ID
|
|||
|
|
const resolvedDependencies: string[] = []
|
|||
|
|
for (const dep of pendingService.dependencies) {
|
|||
|
|
if (typeof dep === 'string') {
|
|||
|
|
resolvedDependencies.push(dep)
|
|||
|
|
} else if (typeof dep === 'function') {
|
|||
|
|
const depServiceId = classToServiceIdMap.get(dep)
|
|||
|
|
if (depServiceId) {
|
|||
|
|
resolvedDependencies.push(depServiceId)
|
|||
|
|
} else {
|
|||
|
|
// 依赖尚未注册,跳过当前服务
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查所有依赖是否都已注册
|
|||
|
|
const allDependenciesRegistered = resolvedDependencies.every(depId =>
|
|||
|
|
container.has(depId)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (allDependenciesRegistered) {
|
|||
|
|
// 注册服务
|
|||
|
|
container.register(
|
|||
|
|
pendingService.serviceId,
|
|||
|
|
(container) => {
|
|||
|
|
// 创建服务实例
|
|||
|
|
const ServiceClass = pendingService.serviceClass
|
|||
|
|
// 使用依赖注入创建实例
|
|||
|
|
const instance = new ServiceClass()
|
|||
|
|
return instance
|
|||
|
|
},
|
|||
|
|
resolvedDependencies
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 从待注册列表中移除
|
|||
|
|
const index = pendingServices.indexOf(pendingService)
|
|||
|
|
if (index > -1) {
|
|||
|
|
pendingServices.splice(index, 1)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
// 服务注册表可能尚未初始化,稍后再试
|
|||
|
|
console.debug('服务注册暂时失败,将在初始化时重试:')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化所有待注册的服务
|
|||
|
|
* 在ServiceProvider初始化后调用
|
|||
|
|
*/
|
|||
|
|
export function initializePendingServices(): void {
|
|||
|
|
// 重试注册所有剩余的服务
|
|||
|
|
while (pendingServices.length > 0) {
|
|||
|
|
const beforeCount = pendingServices.length
|
|||
|
|
tryRegisterPendingServices()
|
|||
|
|
|
|||
|
|
// 如果没有服务被注册,可能存在循环依赖,跳出循环
|
|||
|
|
if (beforeCount === pendingServices.length) {
|
|||
|
|
console.warn('以下服务无法注册,可能存在循环依赖:',
|
|||
|
|
pendingServices.map(s => s.serviceId).join(', '))
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取所有已注册的服务信息
|
|||
|
|
*/
|
|||
|
|
export function getRegisteredServices(): Array<{
|
|||
|
|
serviceId: string
|
|||
|
|
serviceClass: Function
|
|||
|
|
}> {
|
|||
|
|
return Array.from(classToServiceIdMap.entries()).map(([cls, id]) => ({
|
|||
|
|
serviceId: id,
|
|||
|
|
serviceClass: cls
|
|||
|
|
}))
|
|||
|
|
}
|