@clawject/di
Version:
<p align="center"> <a href="https://clawject.com/" target="_blank"><img src="https://clawject.com/img/logo.svg" align="center" alt="Clawject Logo" width="120" height="120" /></a> </p>
201 lines (200 loc) • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApplicationBeanFactory = void 0;
const ApplicationBean_1 = require("./ApplicationBean");
const BeanIdProvider_1 = require("./BeanIdProvider");
const ObjectFactoryImpl_1 = require("../ObjectFactoryImpl");
const MetadataStorage_1 = require("../MetadataStorage");
const ApplicationBeanDependency_1 = require("./ApplicationBeanDependency");
const ApplicationBeanFinder_1 = require("./ApplicationBeanFinder");
const Utils_1 = require("../Utils");
const RuntimeErrors_1 = require("../api/RuntimeErrors");
class ApplicationBeanFactory {
constructor(applicationConfigurationFactory, beanProcessors, scopeRegister) {
this.applicationConfigurationFactory = applicationConfigurationFactory;
this.beanProcessors = beanProcessors;
this.scopeRegister = scopeRegister;
this.exposedBeanNameToApplicationBeanDependency = new Map();
this.applicationBeans = [];
this.configurationIdToBeanClassPropertyToApplicationBean = new Map();
this.scopeToScopedApplicationBeans = new Map();
this.applicationBeanFinder = new ApplicationBeanFinder_1.ApplicationBeanFinder(this.configurationIdToBeanClassPropertyToApplicationBean, applicationConfigurationFactory);
}
async init(applicationMetadata) {
await this.createApplicationBeans(applicationMetadata);
this.fillExposedBeans(applicationMetadata);
await this.initBeans();
}
async postInit() {
const singletonScope = this.scopeRegister.getScope('singleton');
await this.initScopedBeans(singletonScope);
const lifecycleInit = this.applicationBeans.map(async (applicationBean) => {
if (applicationBean.isLifecycleFunction) {
if (applicationBean.parentConfiguration.metadata.lifecycle["POST_CONSTRUCT" /* LifecycleKind.POST_CONSTRUCT */].includes(applicationBean.beanClassProperty)) {
await applicationBean.getInjectionValue();
}
}
});
await Promise.all(lifecycleInit);
}
async destroy() {
await Promise.all(this.applicationBeans.map(async (applicationBean) => {
if (applicationBean.isLifecycleFunction) {
if (applicationBean.parentConfiguration.metadata.lifecycle["PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */].includes(applicationBean.beanClassProperty)) {
await applicationBean.getInjectionValue();
}
}
}));
await Promise.all(this.applicationBeans.map(async (bean) => {
if (bean.isLifecycleFunction) {
return;
}
const beanScope = bean.getScope();
const removedScopedObject = beanScope.remove(bean.name);
if (removedScopedObject === null) {
return;
}
//TODO: check warning
await this.onLifecycle(await removedScopedObject, "PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */);
}));
}
async getExposedBean(beanName) {
const exposedBeans = Utils_1.Utils.getValueSafe(this.exposedBeanNameToApplicationBeanDependency, beanName);
if (exposedBeans === Utils_1.Utils.EMPTY_VALUE) {
throw new RuntimeErrors_1.RuntimeErrors.ExposedBeanNotFoundError(`No exposed bean found by exposed name: ${beanName}`);
}
const value = await exposedBeans.getValue();
return value.value;
}
async getExposedBeans() {
const data = Promise.all(Array.from(this.exposedBeanNameToApplicationBeanDependency.entries())
.map(async ([beanName, exposedBeans]) => [beanName, (await exposedBeans.getValue()).value]));
return Object.fromEntries(await data);
}
async createApplicationBeans(applicationMetadata) {
const resultPromise = this.applicationConfigurationFactory.mapConfigurations(async (applicationConfiguration) => {
const beanDependenciesMetadataByConfiguration = applicationMetadata.beanDependenciesMetadata[applicationConfiguration.index];
if (!beanDependenciesMetadataByConfiguration) {
throw new RuntimeErrors_1.RuntimeErrors.IllegalUsageError('No bean dependencies metadata found');
}
const beanClassPropertyToApplicationBean = new Map();
this.configurationIdToBeanClassPropertyToApplicationBean.set(applicationConfiguration.id, beanClassPropertyToApplicationBean);
const beanNameToDependenciesMetadata = new Map(Object.values(beanDependenciesMetadataByConfiguration.map(it => {
const applicationBeanDependencies = it.dependencies
.map(it => new ApplicationBeanDependency_1.ApplicationBeanDependency(it, this.applicationBeanFinder));
return [it.classPropertyName, applicationBeanDependencies];
})));
const initPromises = Object.entries(applicationConfiguration.metadata.beans).map(async ([beanClassProperty, beanMetadata]) => {
const beanDependenciesMetadata = beanNameToDependenciesMetadata.get(beanClassProperty) ?? null;
let beanClassConstructor = null;
if (beanMetadata.kind === 2 /* BeanKind.CLASS_CONSTRUCTOR */) {
const classProperty = applicationConfiguration.instance[beanClassProperty];
if (Utils_1.Utils.isPromise(classProperty)) {
const resolvedClassProperty = await classProperty;
beanClassConstructor = resolvedClassProperty.constructor;
}
else {
beanClassConstructor = classProperty.constructor;
}
}
const applicationBean = new ApplicationBean_1.ApplicationBean(BeanIdProvider_1.BeanIdProvider.getAndInc(), applicationConfiguration, beanClassProperty, beanMetadata, beanDependenciesMetadata, beanClassConstructor, this.scopeRegister);
beanClassPropertyToApplicationBean.set(beanClassProperty, applicationBean);
this.applicationBeans.push(applicationBean);
if (!applicationBean.isLifecycleFunction) {
const scope = applicationBean.getScope();
const scopedBeans = this.scopeToScopedApplicationBeans.get(scope) ?? [];
if (!this.scopeToScopedApplicationBeans.has(scope)) {
this.scopeToScopedApplicationBeans.set(scope, scopedBeans);
}
scopedBeans.push(applicationBean);
}
});
await Promise.all(initPromises);
});
await Promise.all(resultPromise);
}
async initScopedBeans(scope) {
const scopedBeans = this.scopeToScopedApplicationBeans.get(scope) ?? [];
await Promise.all(scopedBeans.map(async (it) => {
if (it.isLifecycleFunction || it.lazy) {
return;
}
return it.getScopedBeanValue();
}));
}
fillExposedBeans(applicationMetadata) {
applicationMetadata.exposedBeansMetadata.forEach((exposedBeanMetadata) => {
this.exposedBeanNameToApplicationBeanDependency.set(exposedBeanMetadata.qualifiedName, new ApplicationBeanDependency_1.ApplicationBeanDependency(exposedBeanMetadata.metadata, this.applicationBeanFinder));
});
}
async initBeans() {
await Promise.all(this.applicationBeans.map(async (applicationBean) => {
const rawFactory = await this.getBeanFactoryFunction(applicationBean);
let processedFactory = rawFactory;
if (!applicationBean.isLifecycleFunction) {
processedFactory = this.beanProcessors.reduce((acc, processor) => {
if (processor.processFactory) {
return processor.processFactory({
beanMetadata: applicationBean.toMetadata(),
factory: acc,
});
}
return acc;
}, rawFactory);
}
const objectFactory = new ObjectFactoryImpl_1.ObjectFactoryImpl(() => {
return this.createBeanInstance(applicationBean, processedFactory);
});
applicationBean.init(objectFactory);
}));
this.beanProcessors.forEach(processor => processor.onBeansInitialized?.());
}
async createBeanInstance(applicationBean, factory) {
const dependencyInjectionPromises = applicationBean.dependencies?.map(it => {
return it.getValue();
});
const dependencyInjectionValues = await Promise.all(dependencyInjectionPromises ?? []);
const result = await factory(...dependencyInjectionValues.map(it => it.value));
this.registerDestructionCallbackIfNeeded(applicationBean, result);
await this.onLifecycle(result, "POST_CONSTRUCT" /* LifecycleKind.POST_CONSTRUCT */);
return result;
}
getBeanFactoryFunction(applicationBean) {
const configurationInstance = applicationBean.parentConfiguration.instance;
switch (applicationBean.beanMetadata.kind) {
case 1 /* BeanKind.FACTORY_METHOD */:
case 5 /* BeanKind.LIFECYCLE_METHOD */:
return configurationInstance[applicationBean.beanClassProperty].bind(configurationInstance);
case 2 /* BeanKind.CLASS_CONSTRUCTOR */: {
const classPropertyValue = configurationInstance[applicationBean.beanClassProperty];
if (Utils_1.Utils.isPromise(classPropertyValue)) {
return classPropertyValue.then(it => it.factory);
}
return classPropertyValue.factory;
}
case 3 /* BeanKind.FACTORY_ARROW_FUNCTION */:
case 6 /* BeanKind.LIFECYCLE_ARROW_FUNCTION */:
return configurationInstance[applicationBean.beanClassProperty];
case 4 /* BeanKind.VALUE_EXPRESSION */:
return () => configurationInstance[applicationBean.beanClassProperty];
}
}
registerDestructionCallbackIfNeeded(applicationBean, instance) {
if (applicationBean.isLifecycleFunction) {
return;
}
const componentMetadata = MetadataStorage_1.MetadataStorage.getComponentMetadataByClassInstance(instance);
const hasLifecyclePreDestroy = componentMetadata !== null && componentMetadata.lifecycle.PRE_DESTROY.length > 0;
if (hasLifecyclePreDestroy) {
applicationBean.getScope().registerDestructionCallback(applicationBean.name, async () => {
await this.onLifecycle(instance, "PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */);
});
}
}
async onLifecycle(beanInstance, lifecycleKind) {
await Promise.all(MetadataStorage_1.MetadataStorage.getComponentMetadataByClassInstance(beanInstance)?.lifecycle[lifecycleKind].map(async (methodName) => {
await beanInstance[methodName].call(beanInstance);
}) ?? []);
}
}
exports.ApplicationBeanFactory = ApplicationBeanFactory;