Files
vue-desktop/src/services/di/decorators.ts

203 lines
5.8 KiB
TypeScript
Raw Normal View History

2025-10-11 12:05:57 +08:00
/**
*
* 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
}))
}