1.0.11 • Published 6 months ago
@iocore/component v1.0.11
@iocore/component
IoCore 框架的核心组件和依赖注入系统。
提供了一个基于类的依赖注入容器和生命周期管理机制,使用装饰器来定义组件、它们的依赖关系以及初始化和终止逻辑。
安装
npm install @iocore/component reflect-metadata --save
# or
yarn add @iocore/component reflect-metadata依赖
reflect-metadata: 用于支持装饰器元数据。
重要: 你需要在你的应用程序入口文件的顶部导入 reflect-metadata:
import 'reflect-metadata';
// ... 你的其他代码核心概念
Component
所有 IoCore 组件的基础类。任何希望被 IoCore 管理(注入、生命周期)的类都应该直接或间接地继承自 Component。
Application
继承自 Component 的特殊抽象类,代表具有初始化 (initialize) 和终止 (terminate) 生命周期的组件,通常用于表示服务器、数据库连接池等需要启动和停止的服务。
Decorators (装饰器)
@Component.Injectable(callback?: IInjectableCallback): 类装饰器。- 必须 添加到所有希望被 IoCore 管理的
Component类上。 - 可选地接收一个
callback函数(ctx: Context<T>) => unknown | Promise<unknown>。这个回调函数会在组件实例创建 之后,但在注入到其他组件 之前 执行。ctx参数包含:ctx.value: 当前创建的组件实例。ctx.wrap: 组件的包装器实例 (Wrap)。ctx.callbacks: 一个数组,可以在这里push清理函数,这些函数会在组件销毁时执行。
- 必须 添加到所有希望被 IoCore 管理的
@Component.Singleton: 类装饰器。- 标记一个组件为单例。IoCore 容器将只创建该类的一个实例,并在所有注入点共享它。
- 默认情况下,组件是 非单例 的,每次注入都会创建一个新实例 (除非注入的是单例依赖)。
@Component.Inject(clazz: INewAble<T>): 属性装饰器。- 用于声明一个属性依赖。将指定的
clazz(必须是@Component.Injectable的类) 注入到该属性。 - IoCore 会自动处理依赖的实例化和注入。
- 用于声明一个属性依赖。将指定的
@Application.Server: 类装饰器 (快捷方式)。- 必须 添加到继承自
Application的类上。 - 它等同于
@Component.Singleton和@Component.Injectable(ApplicationServer)的组合,确保Application子类是单例,并自动处理initialize和terminate方法的调用。
- 必须 添加到继承自
Meta
一个用于创建和管理装饰器元数据的工具类。它提供了 createClassDecorator, createPropertyDecorator, createMethodDecorator, createParameterDecorator 等静态方法,用于创建自定义装饰器并将元数据附加到类、属性、方法或参数上。
Wrap
内部类,用于包装每个注册的 Component 类。它负责管理组件的元数据、生命周期状态 (status)、依赖关系、上下文 (Context) 和实例化逻辑。
Context
每个组件实例(非单例)或单例组件的包装器 (Wrap) 都有一个关联的 Context。它存储了组件实例 (value) 和销毁时需要执行的回调 (callbacks)。
生命周期
- Preload (
Component.preload(clazz)): 预加载组件类,返回Wrap实例。会递归预加载所有依赖。 - Start (
wrap.start()): 启动组件 (及其依赖)。- 状态变为
INITING。 - 解析并预加载所有
@Inject的依赖项。 - 如果是单例,调用
create()创建实例并缓存。 - 执行
@Injectable的回调函数。 - (对于
Application子类) 调用initialize()方法。 - 状态变为
INTED(成功) 或INTERR(失败)。
- 状态变为
- Create (
wrap.create()或Component.create(clazz)): 创建组件实例。- 如果是单例且已创建,直接返回缓存的实例。
- 创建新实例。
- 递归创建并注入 非单例 依赖的新实例。
- 注入 单例 依赖的共享实例。
- 执行
@Injectable的回调函数。 - 返回实例或
Context。
- Stop (
wrap.stop()): 停止组件。- 状态变为
ENDING。 - 执行
Context.callbacks中注册的清理函数 (按 LIFO 顺序)。 - (对于
Application子类) 调用terminate()方法。 - 清空实例 (如果是单例)。
- 状态变为
ENDED(成功) 或ENDERR(失败)。
- 状态变为
- Terminate (
Application.terminate()): 停止所有已注册的Application组件。
使用示例
import 'reflect-metadata';
import Component, {
Application,
Context,
IInjectableCallback,
} from '@iocore/component';
// --- 定义服务组件 ---
interface IDatabase {
query(sql: string): Promise<string[]>;
}
// 模拟数据库连接 (Application)
@Application.Server // <=> @Component.Singleton + @Component.Injectable(ApplicationServer)
class DatabaseConnection extends Application implements IDatabase {
private connectionId: string;
async initialize(): Promise<void> {
this.connectionId = `conn-${Math.random().toString(36).substring(2, 9)}`;
console.log(`[DatabaseConnection] Initialized, ID: ${this.connectionId}`);
}
async terminate(): Promise<void> {
console.log(`[DatabaseConnection] Terminated, ID: ${this.connectionId}`);
}
async query(sql: string): Promise<string[]> {
console.log(`[DatabaseConnection ${this.connectionId}] Executing query: ${sql}`);
await new Promise(res => setTimeout(res, 50)); // 模拟延迟
return [`Result for: ${sql}`];
}
}
// 服务注入回调示例
const userServiceCallback: IInjectableCallback<UserService> = (ctx) => {
console.log(`[UserService Injectable Callback] UserService instance created. User count: ${ctx.value.getUserCount()}`);
// 注册清理函数
ctx.callbacks.push(() => {
console.log(`[UserService Cleanup] Cleaning up UserService instance.`);
});
};
// 用户服务 (普通 Component,非单例)
@Component.Injectable(userServiceCallback)
class UserService extends Component {
// 注入单例的数据库连接
@Component.Inject(DatabaseConnection)
private db: IDatabase;
private userCount = 0;
constructor() {
super();
this.userCount = Math.floor(Math.random() * 100);
console.log(`[UserService Constructor] Instance created.`);
}
async getUser(id: number): Promise<string> {
const result = await this.db.query(`SELECT * FROM users WHERE id = ${id}`);
return result[0] || 'User not found';
}
getUserCount(): number {
return this.userCount;
}
}
// --- 定义主应用程序 ---
@Application.Server
class MyApp extends Application {
// 注入非单例的 UserService
@Component.Inject(UserService)
private userService1: UserService;
// 再次注入,会得到一个新的 UserService 实例
@Component.Inject(UserService)
private userService2: UserService;
// 注入单例的 DatabaseConnection
@Component.Inject(DatabaseConnection)
private db: IDatabase;
async initialize(): Promise<void> {
console.log('[MyApp] Initialized.');
}
async terminate(): Promise<void> {
console.log('[MyApp] Terminated.');
}
async run() {
console.log('\n--- Running MyApp ---');
console.log('Checking db connection instance:', this.db === (this.userService1 as any).db ? 'Same instance' : 'Different instance'); // true
console.log('Checking user service instances:', this.userService1 === this.userService2 ? 'Same instance' : 'Different instance'); // false
const user1 = await this.userService1.getUser(1);
console.log('User 1:', user1);
console.log('User service 1 count:', this.userService1.getUserCount());
const user2 = await this.userService2.getUser(2);
console.log('User 2:', user2);
console.log('User service 2 count:', this.userService2.getUserCount());
console.log('--- Finished MyApp Run ---\n');
}
}
// --- 启动和停止 --- (通常在入口文件)
async function bootstrap() {
console.log('>>> Bootstrapping application...');
// 预加载并启动 MyApp (会自动启动其依赖)
// 方式一:通过 Component.create (适用于 Application)
const myAppInstance = await Component.create(MyApp);
await myAppInstance.run();
// 方式二:通过 Application.start (更常用,内部调用 preload 和 create)
// const myAppInstance = await Application.start(MyApp);
// await myAppInstance.main(); // 假设 Application 有 main 方法
console.log('\n>>> Tearing down application...');
// 停止所有 Application 组件 (会按依赖反向顺序停止)
await Application.terminate();
console.log('>>> Application terminated.');
}
bootstrap();贡献
欢迎提交 Pull Request。对于重大更改,请先开一个 Issue 来讨论您想要更改的内容。