diff --git a/package.json b/package.json index acba4d0..9fcb997 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "lit": "^3.3.1", "lodash": "^4.17.21", "pinia": "^3.0.3", + "reflect-metadata": "^0.2.2", "uuid": "^11.1.0", "vue": "^3.5.18" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9eaf67d..b737e36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: pinia: specifier: ^3.0.3 version: 3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3)) + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 uuid: specifier: ^11.1.0 version: 11.1.0 @@ -1368,6 +1371,9 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -2902,6 +2908,8 @@ snapshots: readdirp@4.1.2: {} + reflect-metadata@0.2.2: {} + rfdc@1.4.1: {} rollup@4.46.2: diff --git a/src/services/di/ServiceProvider.ts b/src/services/di/ServiceProvider.ts index 22d4193..1a31ade 100644 --- a/src/services/di/ServiceProvider.ts +++ b/src/services/di/ServiceProvider.ts @@ -1,5 +1,6 @@ import { ServiceRegistry, ServiceIds } from './ServiceRegistry' import type { IServiceContainer } from './IServiceContainer' +import { initializePendingServices } from './decorators' /** * 服务提供者 @@ -23,6 +24,9 @@ export class ServiceProvider { this.container = registry.getContainer() this.container.initialize() + + // 初始化所有使用@Service装饰器的服务 + initializePendingServices() this.initialized = true } diff --git a/src/services/di/decorator-example.ts b/src/services/di/decorator-example.ts new file mode 100644 index 0000000..4615fa4 --- /dev/null +++ b/src/services/di/decorator-example.ts @@ -0,0 +1,206 @@ +/** + * 装饰器式依赖注入示例 + * 展示如何使用@Service和@Inject装饰器实现类似Java的自动依赖注入 + */ + +import { Service, Inject } from './decorators' +import { ServiceIds } from './ServiceRegistry' + +// 示例1: 基本的服务定义和注入 + +/** + * 用户服务接口 + */ +interface IUserService { + getUserName(): string + setUserName(name: string): void +} + +/** + * 用户服务实现 + * 使用@Service装饰器标记为可注入服务 + */ +@Service() // 自动使用类名"UserService"作为服务ID +export class UserService implements IUserService { + private userName = 'Default User' + + getUserName(): string { + return this.userName + } + + setUserName(name: string): void { + this.userName = name + } +} + +// 示例2: 带自定义ID和依赖的服务 + +/** + * 日志服务 + */ +@Service({ + id: 'customLoggerService', // 自定义服务ID + dependencies: [] // 无依赖 +}) +export class LoggerService { + log(message: string): void { + console.log(`[Logger] ${new Date().toISOString()}: ${message}`) + } + + error(message: string, error?: Error): void { + console.error(`[Logger] ERROR ${new Date().toISOString()}: ${message}`, error) + } +} + +// 示例3: 具有依赖的服务 + +/** + * 认证服务 + * 依赖于UserService和LoggerService + */ +@Service({ + dependencies: [UserService, 'customLoggerService'] // 可以混合使用类和服务ID +}) +export class AuthService { + // 按类型自动注入 + @Inject() + private userService!: UserService + + // 按服务ID注入 + @Inject('customLoggerService') + private logger!: LoggerService + + // 注入系统内置服务(使用ServiceIds) + @Inject(ServiceIds.EVENT_BUILDER) + private eventBuilder!: any + + authenticate(userName: string): boolean { + this.logger.log(`尝试认证用户: ${userName}`) + + if (userName) { + this.userService.setUserName(userName) + this.logger.log(`认证成功: ${userName}`) + + // 使用事件构建器发送认证成功事件 + if (this.eventBuilder) { + try { + this.eventBuilder.build('auth.success', { userName }) + } catch (error) { + this.logger.error('发送认证事件失败', error) + } + } + + return true + } + + this.logger.error('认证失败: 用户名不能为空') + return false + } +} + +// 示例4: 在组件或业务类中使用 + +/** + * 用户控制器 + * 展示如何在业务类中使用注入的服务 + */ +export class UserController { + // 按类型自动注入 + @Inject() + private authService!: AuthService + + // 按类型自动注入 + @Inject() + private userService!: UserService + + login(userName: string): boolean { + if (this.authService.authenticate(userName)) { + console.log(`用户控制器: ${this.userService.getUserName()} 已登录`) + return true + } + return false + } +} + +// 示例5: 复杂的依赖链 + +/** + * 数据访问服务 + */ +@Service() +export class DataAccessService { + save(data: any): void { + console.log('保存数据:', data) + } + + get(id: string): any { + console.log(`获取数据ID: ${id}`) + return { id, name: '示例数据' } + } +} + +/** + * 用户管理服务 + * 依赖于DataAccessService、UserService和LoggerService + */ +@Service({ + dependencies: [DataAccessService, UserService, LoggerService] +}) +export class UserManagementService { + @Inject() + private dataAccessService!: DataAccessService + + @Inject() + private userService!: UserService + + @Inject() + private loggerService!: LoggerService + + updateUserProfile(userId: string, profile: any): boolean { + try { + this.loggerService.log(`更新用户${userId}的资料`) + const currentUser = this.userService.getUserName() + + const userData = { + ...profile, + userId, + updatedBy: currentUser, + updatedAt: new Date() + } + + this.dataAccessService.save(userData) + this.loggerService.log(`用户${userId}资料更新成功`) + return true + } catch (error) { + this.loggerService.error('更新用户资料失败', error) + return false + } + } +} + +/** + * 运行示例 + * 注意:此函数需要在ServiceProvider初始化后调用 + */ +export async function runDecoratorExample(): Promise { + try { + console.log('\n=== 开始运行装饰器式DI示例 ===') + + // 创建控制器实例 + const userController = new UserController() + + // 测试登录功能 + userController.login('张三') + + // 测试用户管理服务 + const userManagementService = new UserManagementService() + userManagementService.updateUserProfile('user123', { + displayName: '张三', + email: 'zhangsan@example.com' + }) + + console.log('=== 装饰器式DI示例运行完成 ===\n') + } catch (error) { + console.error('示例运行失败:', error) + } +} \ No newline at end of file diff --git a/src/services/di/decorators.ts b/src/services/di/decorators.ts new file mode 100644 index 0000000..3f69be5 --- /dev/null +++ b/src/services/di/decorators.ts @@ -0,0 +1,203 @@ +/** + * 依赖注入装饰器 + * 提供类似Java的自动注入功能 + */ + +import { ServiceProvider } from './ServiceProvider' +import { ServiceRegistry } from './ServiceRegistry' +import 'reflect-metadata'; + +// 定义构造函数类型 +type Constructor = new (...args: any[]) => T + +// 存储类到服务ID的映射 +const classToServiceIdMap = new Map() +// 存储服务ID到类的映射 +const serviceIdToClassMap = new Map() +// 存储需要延迟注册的服务 +const pendingServices: Array<{ + serviceClass: Constructor + serviceId: string + dependencies: Array +}> = [] + +/** + * 服务装饰器 + * 用于标记一个类作为可注入的服务 + * @param options 服务配置选项 + */ +export function Service(options?: { + /** 自定义服务ID,如果不提供则使用类名 */ + id?: string + /** 服务依赖,可以是服务ID字符串或依赖类 */ + dependencies?: Array + /** 是否为单例模式,默认为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 + })) +} \ No newline at end of file diff --git a/src/services/di/types.d.ts b/src/services/di/types.d.ts index d16f33b..794cbcd 100644 --- a/src/services/di/types.d.ts +++ b/src/services/di/types.d.ts @@ -89,3 +89,17 @@ declare module './ServiceProvider' { * 声明Inject装饰器类型 */ declare function Inject(serviceId: T): PropertyDecorator + +/** + * 声明新的Inject装饰器类型,支持类注入 + */ +declare function Inject(dependency?: Function | string): PropertyDecorator + +/** + * 声明Service装饰器类型 + */ +declare function Service(options?: { + id?: string + dependencies?: Array + singleton?: boolean +}): ClassDecorator diff --git a/tsconfig.app.json b/tsconfig.app.json index d292bdb..9a4cafd 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -13,6 +13,7 @@ "module": "ESNext", "strict": true, // 严格模式检查 "experimentalDecorators": true, // 装饰器 + "emitDecoratorMetadata": true, // 元数据反射 "useDefineForClassFields": false, "strictPropertyInitialization": false, // 严格属性初始化检查 "noUnusedLocals": false, // 检查未使用的局部变量