Files
vue-desktop/src/services/di/decorators.ts
2025-10-11 12:05:57 +08:00

203 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 依赖注入装饰器
* 提供类似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
}))
}