@ di- framework/ core (简体中文)
使用装饰器的TypeScript轻量级,型号安全的依赖注射框架. 这个框架自动管理服务即时化,依赖性解析和生命周期管理.
明确的应用程序启动
使用 @Configuration() 财务报告和审定财务报表 @Bean() 用于工厂接线。 依赖是:
明确列出,因此启动检测缺失、重复和循环豆
在援引工厂之前 阿辛克工厂在等待着 Container.resolve()
保持同步状态.
import { ApplicationContext } from '@di-framework/core/application-context';
import { Bean, Configuration } from '@di-framework/core/decorators';
@Configuration()
class AppConfiguration {
@Bean()
port() { return 8080; } // token defaults to "port"
@Bean('serverUrl', { dependencies: ['port'] })
async serverUrl(port: number) { return `http://localhost:${port}`; }
}
const app = ApplicationContext.builder()
.configuration(AppConfiguration)
.bootstrap(HttpServer)
await app.start();
await app.stop();
靴子组件可能定义同步或同步 start() 财务报告和审定财务报表
stop() 钩子. 成功的组件按倒转启动顺序停止 。 遗产
@Bootstrap() 装饰器仍然在类定义时解决,但
被贬后,将在下个大发布会被移除.
安装
不需要外部依赖关系! 该框架与SWC和TypeScript的本土装饰者相配合.
只要确保你有:
- 类型脚本 5.0+
- SWC 或 TypeScript 编译器
experimentalDecorators财务报告和审定财务报表emitDecoratorMetadata启用
装饰者完全融入了SWC的本土支持——不需要 reflect-metadata 或任何其他多填充.
快速启动
1. 基本服务
import { Container } from '@di-framework/core/decorators';
@Container()
export class DatabaseService {
connect(): void {
console.log('Connected to database');
}
}
2. 向受抚养人提供服务
import { Container, Component } from '@di-framework/core/decorators';
import { DatabaseService } from './services/DatabaseService';
@Container()
export class UserService {
@Component(DatabaseService)
private db!: DatabaseService;
constructor() {}
getUser(id: string) {
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
}
}
注:所有附属物都使用属性注射。 这与SWC和TypeScript的本土装饰器支持无缝地工作.
3. 解决服务
import { useContainer } from '@di-framework/core/container';
import { UserService } from './services/UserService';
const container = useContainer();
const userService = container.resolve<UserService>(UserService);
// All dependencies are automatically injected!
userService.getUser('123');
API 参考
@Container(options?)
标出可注射的类别,并自动在 DI 容器中注册.
选项 :
singleton?: boolean(默认 :true- 每次创建新实例或重复使用同一实例container?: DIContainer- 指定自定义容器(默认为全局容器)- 注:导入为
import { Container as DIContainer } from '@di-framework/core/container'避免名称与@Container装饰师.
- 注:导入为
示例
@Container({ singleton: false })
export class RequestScopedService {
// New instance created for each resolution
}
@Component(target)
标记依赖性注入的构造参数或属性.
参数:
target- 为工厂注册服务注入的类别或字符串标识符
示例 - 构造参数:
@Container()
export class OrderService {
constructor(@Component(DatabaseService) private db: DatabaseService) {}
}
示例 - 属性注入 :
@Container()
export class ReportService {
@Component(DatabaseService)
private db: DatabaseService;
}
@Telemetry(options?)
标记一种遥测跟踪方法。 当呼叫时,它发出一个 telemetry 在容器上的活动。 既采用同步方法,又采用同步方法.
选项 :
logging?: boolean(默认 :false) - 如果真实,将方法执行细节(状态和持续时间)记录到控制台.
示例
@Container()
export class ApiService {
@Telemetry({ logging: true })
async fetchData(id: string) {
// ...
}
}
@TelemetryListener()
将一种方法标为遥测事件的听众。 该方法将自动注册到容器的 telemetry 当服务被即时化时.
示例
@Container()
export class MonitoringService {
@TelemetryListener()
onTelemetry(event: any) {
console.log(
`Method ${event.className}.${event.methodName} took ${event.endTime - event.startTime}ms`,
);
}
}
useContainer()
返回全局 DI 容器实例.
import { useContainer } from '@di-framework/core/container';
const container = useContainer();
container.register(serviceClass, options?)
手动注册服务类.
container.register(UserService, { singleton: true });
container.registerFactory(name, factory, options?)
使用工厂功能注册服务.
container.registerFactory(
'config',
() => ({
apiKey: process.env.API_KEY,
dbUrl: process.env.DATABASE_URL,
}),
{ singleton: true },
);
container.resolve(serviceClass)
解决并获得一个服务的例子.
const userService = container.resolve<UserService>(UserService);
// or by name
const config = container.resolve('config');
container.has(serviceClass)
检查服务是否注册 .
if (container.has(UserService)) {
const service = container.resolve(UserService);
}
container.getServiceNames()
获取所有注册服务名 .
const names = container.getServiceNames();
console.log(names); // ['DatabaseService', 'UserService', ...]
container.on(event, listener)
订阅DI容器生命周期事件(观察者模式).
活动 :
registered- 某类或工厂注册时被解雇resolved- 服务一经解决即被开除(缝合或新鲜)constructed- 被开除时construct()创建新实例cleared- 集装箱清空后发射
示例
const unsubscribe = container.on('resolved', ({ key, fromCache }) => {
console.log(`Resolved ${typeof key === 'string' ? key : key.name} (fromCache=${fromCache})`);
});
unsubscribe(); // stop listening
container.construct(serviceClass, overrides?)
在不注册的情况下创建新实例,同时尊重依赖性注射。 用于构建者- 平面方案, 您需要提供特定的原始/ 配置值 .
import { Component } from '@di-framework/core/decorators';
import { container } from '@di-framework/core/container';
class LoggerService {
log(message: string) {
console.log(message);
}
}
class Greeter {
constructor(
@Component(LoggerService) private logger: LoggerService,
private greeting: string,
) {}
}
const greeter = container.construct(Greeter, { 1: 'hello world' });
container.fork(options?)
将集装箱登记(原型型)划入一个新的集装箱。 过 { carrySingletons: true } 以重用已有的单子实例;默认从新实例开始.
const testContainer = container.fork({ carrySingletons: false });
高级示例
多重依赖
@Container()
export class ApplicationContext {
constructor(
@Component(DatabaseService) private db: DatabaseService,
@Component(LoggerService) private logger: LoggerService,
@Component(AuthService) private auth: AuthService,
) {}
async initialize() {
this.logger.log('Initializing application...');
await this.db.connect();
this.auth.setup();
}
}
临时(非英语)服务
@Container({ singleton: false })
export class RequestContext {
id = Math.random().toString();
constructor(@Component(LoggerService) private logger: LoggerService) {
this.logger.log(`Request context created: ${this.id}`);
}
}
// Each resolve creates a new instance
const ctx1 = container.resolve(RequestContext); // new instance
const ctx2 = container.resolve(RequestContext); // different instance
生命周期方法
服务可选择采用生命周期方法:
@Container()
export class DatabaseService {
private connected = false;
setEnv(env: Record<string, any>) {
// Called to initialize environment-specific config
console.log('DB URL:', env.DATABASE_URL);
}
setCtx(context: any) {
// Called to set execution context
console.log('Context:', context);
}
connect() {
this.connected = true;
}
}
// Calling lifecycle methods
const db = container.resolve(DatabaseService);
db.setEnv(process.env);
db.setCtx({ userId: '123' });
db.connect();
工厂功能
container.registerFactory(
'apiClient',
() => {
return new HttpClient({
baseUrl: process.env.API_URL,
timeout: 5000,
});
},
{ singleton: true },
);
// Use in services
@Container()
export class UserService {
constructor(@Component('apiClient') private api: any) {}
}
如何运作
- 装饰: 当你装饰一个类
@Container(),装饰者用全球容器进行注册 - 登记: 分类存储在容器中, 包含关于依赖性的元数据
- 决议: 当你打电话时
container.resolve(ServiceClass)数字 :- 容器创建新实例( 或返回已存在的单吨)
- 它研究了构造参数及其类型
- 它递相解决了每个依赖
- 依赖性注入到构造器中
- 返回已配置实例
- 缓存: Singleton 实例被缓存并重新使用
与 SAMPLE.ts 的比较
之前( 手册- SAMPLE. ts)
const createServerContext = (env, ctx) => {
if (!instanceState.member) {
const contextInstance = Context.create({
contactService: ContactService.create({}),
assetService: AssetService.create({}),
transactionService: TransactionService.create({}),
// ... 20+ more services manually created and wired
chatService: ChatService.create({
openAIApiKey: env.OPENAI_API_KEY,
// ... manual configuration
}),
});
instanceState.member = contextInstance;
}
instanceState.member.setEnv(env);
instanceState.member.setCtx(ctx);
// ... manual dependency wiring
instanceState.member.knowledgeService.setAttachmentService(
instanceState.member.attachmentService,
);
return instanceState.member;
};
之后( DI 框架)
@Container()
export class ApplicationContext {
constructor(
@Component(ContactService) private contactService: ContactService,
@Component(AssetService) private assetService: AssetService,
@Component(TransactionService)
private transactionService: TransactionService,
// ... all services automatically injected
@Component(ChatService) private chatService: ChatService,
) {}
setEnv(env: Record<string, any>) {
// Services are already available via constructor injection
this.chatService.initialize(env.OPENAI_API_KEY);
}
setCtx(ctx: any) {
// All services have access to context
}
}
// Usage
const container = useContainer();
const appContext = container.resolve(ApplicationContext);
appContext.setEnv(env);
appContext.setCtx(ctx);
福利:
- 没有手动服务即时
- 没有手动依赖线
- 自动单通管理
- 类型安全依赖性解决
- 便于测试(仅通过登记测试实施情况提供模拟服务)
- 随着服务的增长,规模会更好
处理错误
循环依赖
// This will be detected and throw an error:
@Container()
class ServiceA {
constructor(@Component(ServiceB) private b: ServiceB) {}
}
@Container()
class ServiceB {
constructor(@Component(ServiceA) private a: ServiceA) {}
}
// Error: Circular dependency detected while resolving ServiceA
未登记的服务
@Container()
class MyService {
constructor(@Component(UnregisteredService) private s: UnregisteredService) {}
}
// Error: Service 'UnregisteredService' is not registered in the DI container
最佳做法
- 将所有服务标为
@Container()- 明确扶养管理 - 使用构造器注入 - 强制性依赖性财产注入优先
- 为可选依赖性使用属性注入 - 尽量少
- 不需要导入反射元数据 - 这个框架使用轻量级元数据存储
- 服务接口与执行分离 - 方便测试
- 无国籍人服务使用单数 - 多数服务应为单吨
- 使用瞬态(非 singleton)进行状态服务 - 请求/会期服务
测试
// Create a test container
import { Container as DIContainer } from '@di-framework/core/container';
const testContainer = new DIContainer();
// Register mock implementations
class MockDatabaseService {
query() {
return { mock: true };
}
}
testContainer.register(MockDatabaseService);
// Register dependencies
testContainer.register(UserService);
// Test the service with mocked dependencies
const userService = testContainer.resolve(UserService);
expect(userService.getUser('1')).toEqual({ mock: true });
许可证
私营服务对服务的约束
宣布私人服务对服务的约束,因此服务可以输出可调用合同,经授权的呼叫者可以通过DI通过命名的依赖引用这些合同,而不会暴露公共HTTP端点或网络地址.
1. 申报出口服务
import { Container } from '@di-framework/core/decorators';
import { ExportService } from '@di-framework/core/service-bindings';
@Container()
@ExportService({
name: 'inventory-service',
operations: ['reserve', 'release'],
})
export class InventoryService {
async reserve(items: any[]) {
return { reservationId: 'res-123' };
}
async release(reservationId: string) {
return { released: true };
}
}
何时 operations 被省略,类注册在不构建服务的情况下发现原型方法. 明确宣告箭头函数字段,例如 @ExportService({ name: 'inventory-service', operations: ['read'] }) 页:1 read = () => ...当操作启动时,容器解决服务实例。 注册一个已存在的实例也会发现它自己的函数属性.
2. 向打电话者发送装订服务
import { Container } from '@di-framework/core/decorators';
import { ServiceBinding } from '@di-framework/core/service-bindings';
@Container()
export class CheckoutService {
constructor(
@ServiceBinding('inventory', {
caller: 'checkout-service',
target: 'inventory-service',
})
private readonly inventory: InventoryContract,
) {}
async checkout(items: any[]) {
return await this.inventory.reserve(items);
}
}
3. 拒绝不受约束的打电话者
未获得明确授权的来电者不得援引目标服务并被拒绝 UnboundCallerError。 。 。 .
4. 地方多服务发展和移动替代
运行多个本地服务, 监视绑定状态
import { LocalServiceDevManager } from '@di-framework/core/service-bindings';
const dev = new LocalServiceDevManager();
dev.registerService('inventory-service', new InventoryService());
dev.bind('checkout-service', 'inventory', 'inventory-service');
console.log(dev.formatStatusTable());